From: Pekka Riikonen Date: Mon, 6 May 2002 20:11:02 +0000 (+0000) Subject: updates. X-Git-Tag: silc.toolkit.0.9~10 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=f82024f04b73e3b80ddd6c590699206d3dc5c1eb updates. --- diff --git a/CHANGES b/CHANGES index 8388cc15..688fb222 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,32 @@ +Mon May 6 19:46:12 EEST 2002 Pekka Riikonen + + * Added silc_pkcs_public_key_copy function into the + lib/silccrypt/silcpkcs.[ch]. + + * Remove the `iv' from the SilcChannelEntry since we can + live without it. Affected files are silcd/idlist.h and + silcd/packet_receive.c. + + * Added support for sending the founder's public key in + the CMODE_CHANGE notify packet in the server. Affected + files are silcd/packet_send.[ch], silcd/packet_receive.c, + silcd/command.c and silcd/server.c. + + * Changed the FOUNDER_AUTH authentication to use only + public key authentication as defined by new protocol + specs. Passphrase authentication with that mode cannot + be used anymore. It is now possible to reclaim founder + mode from any server in the network. Affected files are + silcd/command.c, silcd/idlist.h and silcd/command_reply.c. + + * Added permanent channels support by making the channel + permanent when FOUNDER_AUTH mode is set on the channel. + The channel will not be destroyed even if channel is empty + when that mode is set. Protocol TODO #17. Affected + files are silcd/server.[ch], server_util.[ch], + silcd/command.c, silcd/packet_receive.c and + lib/silcclient/command.c. + Fri May 3 18:36:51 EEST 2002 Pekka Riikonen * Added reference counter to the command reply context in diff --git a/TODO b/TODO index 77cf72b4..752dc8ec 100644 --- a/TODO +++ b/TODO @@ -24,12 +24,6 @@ TODO/bugs In SILC Server for single IP address, key exchange frequency, key exchange frequency for single IP. Add also frequency base. - o If server send CUMODE_CHANGE notify (like setting founder) to router - and router does not have founder on channel (founder is left or there's - no founder on channel at all), the router will accept the server's - founder mode change, even though it perhaps should not do that (Fix - this to 0.9). - o Implement the SILC_CUMODE_QUIET user mode (Do this to 0.9.x). o Implement the and the Attribute Payload to @@ -90,10 +84,3 @@ Manual (Do these to 0.9 and 1.0). o Write "Platform Implementations" document to describe what platforms Toolkit support, what has been implemented, what has not been, what works differently etc. - - -TODO in SILC Protocol -===================== - - 17. Cell wide channel founder support, and permanent channels when - founder mode set. diff --git a/apps/irssi/docs/help/in/cmode.in b/apps/irssi/docs/help/in/cmode.in index b9e49b81..6f4f25db 100644 --- a/apps/irssi/docs/help/in/cmode.in +++ b/apps/irssi/docs/help/in/cmode.in @@ -24,18 +24,16 @@ option(s). The following modes are available: be provided when joining to the channel. c Set/unset channel's cipher h Set/unset channel's hmac - f <-pubkey| - Set/unset channel founder authentication. + f Set/unset channel founder authentication. Channel founder may set this mode so that if the client leaves the channel it can claim the founder rights when it returns - to the channel. If -pubkey is set then - the authentication will be done using the - client's public key. You can claim the - founder rights using the CUMODE command. + to the channel, and to set the channel to + be permanent channel. You can claim the + founder rights using CUMODE or JOIN commands. Multiple modes can be set/unset at once if the modes does not require any arguments. If mode requires an argument then only one mode can be set at once. -See also: CUMODE, UMODE +See also: CUMODE, UMODE, JOIN diff --git a/apps/irssi/docs/help/in/cumode.in b/apps/irssi/docs/help/in/cumode.in index 84d0e208..ba881190 100644 --- a/apps/irssi/docs/help/in/cumode.in +++ b/apps/irssi/docs/help/in/cumode.in @@ -14,17 +14,14 @@ are available: both founder and operator rights, can be used only to remove both modes at once). - f [@] [-pubkey|] + f [@] [-pubkey] Set/Unset channel founder. If the -pubkey option or is provided then the client is claiming the founder rights by - providing the channel founder authentication - data. If the -pubkey is provided then the - authentication is performed using the - client's public key. If you are channel - founder you can set the channel founder - authentication using CMODE command. + providing your public key as argument. If + you are channel founder you can set the channel + founder authentication using CMODEc command. o [@] diff --git a/apps/silcd/command.c b/apps/silcd/command.c index cedebbd4..653996dc 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -3221,16 +3221,9 @@ static void silc_server_command_join_channel(SilcServer server, if (channel->founder_key && idata->public_key && silc_pkcs_public_key_compare(channel->founder_key, idata->public_key)) { - void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ? - (void *)channel->founder_passwd : - (void *)channel->founder_key); - SilcUInt32 auth_data_len = - (channel->founder_method == SILC_AUTH_PASSWORD ? - channel->founder_passwd_len : 0); - /* Check whether the client is to become founder */ - if (silc_auth_verify_data(auth, auth_len, channel->founder_method, - auth_data, auth_data_len, + if (silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY, + channel->founder_key, 0, idata->hash, client->id, SILC_ID_CLIENT)) { umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); founder = TRUE; @@ -3350,6 +3343,7 @@ static void silc_server_command_join_channel(SilcServer server, silc_hash_table_add(channel->user_list, client, chl); silc_hash_table_add(client->channels, channel, chl); channel->user_count++; + channel->disabled = FALSE; /* Get users on the channel */ silc_server_get_users_on_channel(server, channel, &user_list, &mode_list, @@ -3526,7 +3520,8 @@ SILC_SERVER_CMD_FUNC(join) SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data; client_id = silc_id_dup(entry->id, SILC_ID_CLIENT); - if (!channel || channel->disabled) { + if (!channel || + (channel->disabled && server->server_type != SILC_ROUTER)) { /* Channel not found */ /* If we are standalone server we don't have a router, we just create @@ -3655,7 +3650,8 @@ SILC_SERVER_CMD_FUNC(join) /* If the channel does not have global users and is also empty the client will be the channel founder and operator. */ - if (!channel->global_users && !silc_hash_table_count(channel->user_list)) + if (!channel->disabled && + !channel->global_users && !silc_hash_table_count(channel->user_list)) umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); /* Join to the channel */ @@ -3898,6 +3894,9 @@ SILC_SERVER_CMD_FUNC(cmode) SilcUInt32 mode_mask = 0, tmp_len, tmp_len2; SilcUInt16 ident = silc_command_get_ident(cmd->payload); bool set_mask = FALSE; + SilcPublicKey founder_key = NULL; + unsigned char *fkey = NULL; + SilcUInt32 fkey_len = 0; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 1, 7); @@ -4184,46 +4183,28 @@ SILC_SERVER_CMD_FUNC(cmode) if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) { if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) { /* Set the founder authentication */ - SilcAuthPayload auth; - tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len); if (!tmp) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_CMODE, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); goto out; } - auth = silc_auth_payload_parse(tmp, tmp_len); - if (!auth) { + /* Verify the payload before setting the mode */ + if (!silc_auth_verify_data(tmp, tmp_len, SILC_AUTH_PUBLIC_KEY, + idata->public_key, 0, idata->hash, + client->id, SILC_ID_CLIENT)) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + SILC_STATUS_ERR_AUTH_FAILED, + 0); goto out; } /* Save the public key */ - tmp = silc_pkcs_public_key_encode(idata->public_key, &tmp_len); - silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key); - silc_free(tmp); - - channel->founder_method = silc_auth_get_method(auth); - - if (channel->founder_method == SILC_AUTH_PASSWORD) { - tmp = silc_auth_get_data(auth, &tmp_len); - channel->founder_passwd = silc_memdup(tmp, tmp_len); - channel->founder_passwd_len = tmp_len; - } else { - /* Verify the payload before setting the mode */ - if (!silc_auth_verify(auth, channel->founder_method, - channel->founder_key, 0, idata->hash, - client->id, SILC_ID_CLIENT)) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, - SILC_STATUS_ERR_AUTH_FAILED, - 0); - goto out; - } - } - - silc_auth_payload_free(auth); + channel->founder_key = silc_pkcs_public_key_copy(idata->public_key); + founder_key = channel->founder_key; + fkey = silc_pkcs_public_key_encode(founder_key, &fkey_len); } } } else { @@ -4231,10 +4212,7 @@ SILC_SERVER_CMD_FUNC(cmode) if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { if (channel->founder_key) silc_pkcs_public_key_free(channel->founder_key); - if (channel->founder_passwd) { - silc_free(channel->founder_passwd); - channel->founder_passwd = NULL; - } + channel->founder_key = NULL; } } } @@ -4245,13 +4223,14 @@ SILC_SERVER_CMD_FUNC(cmode) /* Send CMODE_CHANGE notify. */ cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); silc_server_send_notify_to_channel(server, NULL, channel, FALSE, - SILC_NOTIFY_TYPE_CMODE_CHANGE, 5, + SILC_NOTIFY_TYPE_CMODE_CHANGE, 6, cidp->data, cidp->len, tmp_mask, 4, cipher, cipher ? strlen(cipher) : 0, hmac, hmac ? strlen(hmac) : 0, passphrase, passphrase ? - strlen(passphrase) : 0); + strlen(passphrase) : 0, + fkey, fkey_len); /* Set CMODE notify type to network */ if (!server->standalone) @@ -4259,7 +4238,7 @@ SILC_SERVER_CMD_FUNC(cmode) server->server_type == SILC_ROUTER ? TRUE : FALSE, channel, mode_mask, client->id, SILC_ID_CLIENT, - cipher, hmac, passphrase); + cipher, hmac, passphrase, founder_key); /* Send command reply to sender */ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE, @@ -4273,6 +4252,7 @@ SILC_SERVER_CMD_FUNC(cmode) silc_buffer_free(cidp); out: + silc_free(fkey); silc_free(channel_id); silc_server_command_free(cmd); } @@ -4406,38 +4386,33 @@ SILC_SERVER_CMD_FUNC(cumode) if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) { /* The client tries to claim the founder rights. */ unsigned char *tmp_auth; - SilcUInt32 tmp_auth_len, auth_len; - void *auth; - + SilcUInt32 tmp_auth_len; + if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) || !channel->founder_key || !idata->public_key || !silc_pkcs_public_key_compare(channel->founder_key, idata->public_key)) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE, - SILC_STATUS_ERR_NOT_YOU, 0); + SILC_STATUS_ERR_AUTH_FAILED, 0); goto out; } tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len); if (!tmp_auth) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + SILC_STATUS_ERR_AUTH_FAILED, 0); goto out; } - auth = (channel->founder_method == SILC_AUTH_PASSWORD ? - (void *)channel->founder_passwd : (void *)channel->founder_key); - auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ? - channel->founder_passwd_len : 0); - - if (!silc_auth_verify_data(tmp_auth, tmp_auth_len, - channel->founder_method, auth, auth_len, - idata->hash, client->id, SILC_ID_CLIENT)) { + /* Verify the authentication payload */ + if (!silc_auth_verify_data(tmp_auth, tmp_auth_len, SILC_AUTH_PUBLIC_KEY, + channel->founder_key, 0, idata->hash, + client->id, SILC_ID_CLIENT)) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE, SILC_STATUS_ERR_AUTH_FAILED, 0); goto out; } - + sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO; notify = TRUE; } diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 9b344cda..3696ec3a 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -899,8 +899,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join) them in the entry */ if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) { silc_pkcs_public_key_free(entry->founder_key); - silc_free(entry->founder_passwd); - entry->founder_passwd = NULL; + entry->founder_key = NULL; } } diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index 211d59eb..9503742f 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -663,6 +663,8 @@ int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry) silc_hmac_free(entry->hmac); silc_free(entry->hmac_name); silc_free(entry->rekey); + if (entry->founder_key) + silc_pkcs_public_key_free(entry->founder_key); memset(entry, 'F', sizeof(*entry)); silc_free(entry); diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index c9fa2fa7..e2acc329 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -415,14 +415,10 @@ struct SilcClientEntryStruct { the cipher to be used. This can be set at SILC_COMMAND_JOIN. SilcPublicKey founder_key - SilcAuthMethod founder_method - unsigned char *founder_passwd - SilcUInt32 founder_passwd_len - If the SILC_CMODE_FOUNDER_AUTH has been set then these will include - the founder's public key, authentication method and the password - if the method is SILC_AUTH_PASSWORD. If it is SILC_AUTH_PUBLIC_KEY - then the `founder_passwd' is NULL. + If the SILC_CMODE_FOUNDER_AUTH has been set then this will include + the founder's public key. When the mode and this key is set the + channel is also permanent channel and cannot be destroyed. SilcHashTable user_list @@ -463,15 +459,10 @@ struct SilcChannelEntryStruct { char *channel_name; SilcUInt32 mode; SilcChannelID *id; - bool global_users; char *topic; char *cipher; char *hmac_name; - SilcPublicKey founder_key; - SilcAuthMethod founder_method; - unsigned char *founder_passwd; - SilcUInt32 founder_passwd_len; SilcUInt32 user_limit; unsigned char *passphrase; @@ -489,14 +480,15 @@ struct SilcChannelEntryStruct { SilcCipher channel_key; unsigned char *key; SilcUInt32 key_len; - unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; SilcHmac hmac; SilcServerChannelRekey rekey; - unsigned long created; - bool disabled; - bool users_resolved; + + /* Flags */ + unsigned int global_users : 1; + unsigned int disabled : 1; + unsigned int users_resolved : 1; }; /* diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 20dd6210..03fcde91 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -575,9 +575,6 @@ void silc_server_notify(SilcServer server, FALSE : !server->standalone); } - /* Change mode */ - channel->mode = mode; - /* Get the hmac */ tmp = silc_argument_get_arg_type(args, 4, &tmp_len); if (tmp) { @@ -604,6 +601,48 @@ void silc_server_notify(SilcServer server, channel->passphrase = silc_memdup(tmp, tmp_len); } + /* Get founder public key */ + tmp = silc_argument_get_arg_type(args, 6, &tmp_len); + if (tmp && mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { + if (channel->founder_key) + silc_pkcs_public_key_free(channel->founder_key); + channel->founder_key = NULL; + silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key); + + if (!channel->founder_key || + (client && client->data.public_key && + server->server_type == SILC_ROUTER && + !silc_pkcs_public_key_compare(channel->founder_key, + client->data.public_key))) { + /* A really buggy server isn't checking public keys correctly. + It's not possible that the mode setter and founder wouldn't + have same public key. */ + SILC_LOG_DEBUG(("Enforcing sender to change channel mode")); + + mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH; + silc_server_send_notify_cmode(server, sock, FALSE, channel, + mode, server->id, SILC_ID_SERVER, + channel->cipher, + channel->hmac_name, + channel->passphrase, NULL); + if (channel->founder_key) + silc_pkcs_public_key_free(channel->founder_key); + channel->founder_key = NULL; + } else if (!client->data.public_key) { + client->data.public_key = + silc_pkcs_public_key_copy(channel->founder_key); + } + } + + /* Change mode */ + channel->mode = mode; + + if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && + channel->founder_key) { + silc_pkcs_public_key_free(channel->founder_key); + channel->founder_key = NULL; + } + break; case SILC_NOTIFY_TYPE_CUMODE_CHANGE: @@ -713,66 +752,53 @@ void silc_server_notify(SilcServer server, } } - /* Get entry to the channel user list */ - silc_hash_table_list(channel->user_list, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chl)) { - /* If the mode is channel founder and we already find a client - to have that mode on the channel we will enforce the sender - to change the channel founder mode away. There can be only one - channel founder on the channel. */ - if (server->server_type == SILC_ROUTER && - mode & SILC_CHANNEL_UMODE_CHANFO && - chl->mode & SILC_CHANNEL_UMODE_CHANFO) { - SilcBuffer idp; - unsigned char cumode[4]; - - if (chl->client == client && chl->mode == mode) { - notify_sent = TRUE; - break; - } - + if (mode & SILC_CHANNEL_UMODE_CHANFO && + !(chl->mode & SILC_CHANNEL_UMODE_CHANFO) && + server->server_type == SILC_ROUTER) { + /* Check whether this client is allowed to be channel founder on + this channel. */ + + /* If channel doesn't have founder auth mode then it's impossible + that someone would be getting founder rights with CUMODE command. + In that case there already either is founder or there isn't + founder at all on the channel. */ + if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) { + /* Force the mode to not have founder mode */ mode &= ~SILC_CHANNEL_UMODE_CHANFO; - silc_server_send_notify_cumode(server, sock, FALSE, channel, mode, - client2->id, SILC_ID_CLIENT, - client2->id); - - idp = silc_id_payload_encode(client2->id, SILC_ID_CLIENT); - SILC_PUT32_MSB(mode, cumode); - silc_server_send_notify_to_channel(server, sock, channel, FALSE, - SILC_NOTIFY_TYPE_CUMODE_CHANGE, - 3, idp->data, idp->len, - cumode, 4, - idp->data, idp->len); - silc_buffer_free(idp); + silc_server_force_cumode_change(server, sock, channel, chl, mode); notify_sent = TRUE; - - /* Force the mode change if we alredy set the mode */ - if (chl2) { - chl2->mode = mode; - silc_free(channel_id); - silc_hash_table_list_reset(&htl); - goto out; - } + break; } - - if (chl->client == client2) { - if (chl->mode == mode) { + + /* Get the founder of the channel and if found then this client + cannot be the founder since there already is one. */ + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chl2)) + if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) { + mode &= ~SILC_CHANNEL_UMODE_CHANFO; + silc_server_force_cumode_change(server, sock, channel, chl, mode); notify_sent = TRUE; break; } + silc_hash_table_list_reset(&htl); + if (!(mode & SILC_CHANNEL_UMODE_CHANFO)) + break; + + /* XXX Founder not found of the channel. Since the founder auth mode + is set on the channel now check whether this is the client that + originally set the mode. If we don't have the public key it + is resolved first. + if (!silc_pkcs_public_key_compare(channel->founder_key, + client->data.public_key)) + */ + + } - SILC_LOG_DEBUG(("Changing the channel user mode")); + SILC_LOG_DEBUG(("Changing the channel user mode")); + + /* Change the mode */ + chl->mode = mode; - /* Change the mode */ - chl->mode = mode; - if (!(mode & SILC_CHANNEL_UMODE_CHANFO)) - break; - - chl2 = chl; - } - } - silc_hash_table_list_reset(&htl); - /* Send the same notify to the channel */ if (!notify_sent) silc_server_packet_send_to_channel(server, sock, channel, @@ -2627,14 +2653,14 @@ void silc_server_new_channel(SilcServer server, SILC_LOG_DEBUG(("Forcing the server to change channel mode")); silc_server_send_notify_cmode(server, sock, FALSE, channel, channel->mode, server->id, - SILC_ID_SERVER, - channel->cipher, channel->hmac_name, - channel->passphrase); + SILC_ID_SERVER, channel->cipher, + channel->hmac_name, + channel->passphrase, + channel->founder_key); } /* Create new key for the channel and send it to the server and everybody else possibly on the channel. */ - if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { if (!silc_server_create_channel_key(server, channel, 0)) return; diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index 85e7695d..3836144f 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -683,15 +683,16 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server, channel->channel_key) { SilcUInt32 mac_len = silc_hmac_len(channel->hmac); SilcUInt32 iv_len = silc_cipher_get_block_len(channel->channel_key); + unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; if (data_len <= mac_len + iv_len) { SILC_LOG_WARNING(("Corrupted channel message, cannot relay it")); return FALSE; } - memcpy(channel->iv, data + (data_len - iv_len), iv_len); + memcpy(iv, data + (data_len - iv_len), iv_len); silc_channel_message_payload_encrypt(data, data_len - iv_len - mac_len, - data_len, channel->iv, iv_len, + data_len, iv, iv_len, channel->channel_key, channel->hmac); } @@ -1174,14 +1175,18 @@ void silc_server_send_notify_cmode(SilcServer server, SilcChannelEntry channel, SilcUInt32 mode_mask, void *id, SilcIdType id_type, - char *cipher, char *hmac, - char *passphrase) + const char *cipher, const char *hmac, + const char *passphrase, + SilcPublicKey founder_key) { SilcBuffer idp; - unsigned char mode[4]; + unsigned char mode[4], *key = NULL; + SilcUInt32 key_len = 0; idp = silc_id_payload_encode((void *)id, id_type); SILC_PUT32_MSB(mode_mask, mode); + if (founder_key) + key = silc_pkcs_public_key_encode(founder_key, &key_len); silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id, SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_CMODE_CHANGE, @@ -1190,11 +1195,13 @@ void silc_server_send_notify_cmode(SilcServer server, cipher, cipher ? strlen(cipher) : 0, hmac, hmac ? strlen(hmac) : 0, passphrase, passphrase ? - strlen(passphrase) : 0); + strlen(passphrase) : 0, + key, key_len); + silc_free(key); silc_buffer_free(idp); } -/* Sends CUMODE_CHANGE notify type. This tells that `client_id' changed the +/* Sends CUMODE_CHANGE notify type. This tells that `id' changed the `target' client's mode on `channel'. The notify packet is always destined to the channel. */ diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h index 2c322e20..491fc5db 100644 --- a/apps/silcd/packet_send.h +++ b/apps/silcd/packet_send.h @@ -141,8 +141,9 @@ void silc_server_send_notify_cmode(SilcServer server, SilcChannelEntry channel, SilcUInt32 mode_mask, void *id, SilcIdType id_type, - char *cipher, char *hmac, - char *passphrase); + const char *cipher, const char *hmac, + const char *passphrase, + SilcPublicKey founder_key); void silc_server_send_notify_cumode(SilcServer server, SilcSocketConnection sock, bool broadcast, diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 6037be57..beecd96a 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -2729,9 +2729,9 @@ void silc_server_free_sock_user_data(SilcServer server, void silc_server_remove_from_channels(SilcServer server, SilcSocketConnection sock, SilcClientEntry client, - int notify, - char *signoff_message, - int keygen) + bool notify, + const char *signoff_message, + bool keygen) { SilcChannelEntry channel; SilcChannelClientEntry chl; @@ -2744,6 +2744,8 @@ void silc_server_remove_from_channels(SilcServer server, return; clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); + if (!clidp) + notify = FALSE; /* Remove the client from all channels. The client is removed from the channels' user list. */ @@ -2751,22 +2753,15 @@ void silc_server_remove_from_channels(SilcServer server, while (silc_hash_table_get(&htl, NULL, (void *)&chl)) { channel = chl->channel; - /* Remove channel from client's channel list */ - silc_hash_table_del(client->channels, channel); - - /* Remove channel if there is no users anymore */ + /* Remove channel if this is last client leaving the channel, unless + the channel is permanent. */ if (server->server_type == SILC_ROUTER && silc_hash_table_count(channel->user_list) < 2) { - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - if (silc_idlist_del_channel(server->local_list, channel)) - server->stat.my_channels--; - else - silc_idlist_del_channel(server->global_list, channel); + silc_server_channel_delete(server, channel); continue; } - /* Remove client from channel's client list */ + silc_hash_table_del(client->channels, channel); silc_hash_table_del(channel->user_list, chl->client); channel->user_count--; @@ -2780,7 +2775,8 @@ void silc_server_remove_from_channels(SilcServer server, server->stat.my_chanclients--; /* If there is not at least one local user on the channel then we don't - need the channel entry anymore, we can remove it safely. */ + need the channel entry anymore, we can remove it safely, unless the + channel is permanent channel */ if (server->server_type != SILC_ROUTER && !silc_server_channel_has_local(channel)) { /* Notify about leaving client if this channel has global users. */ @@ -2792,37 +2788,13 @@ void silc_server_remove_from_channels(SilcServer server, signoff_message, signoff_message ? strlen(signoff_message) : 0); - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - - if (channel->founder_key) { - /* The founder auth data exists, do not remove the channel entry */ - SilcChannelClientEntry chl2; - SilcHashTableList htl2; - - channel->disabled = TRUE; - - silc_hash_table_list(channel->user_list, &htl2); - while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) { - silc_hash_table_del(chl2->client->channels, channel); - silc_hash_table_del(channel->user_list, chl2->client); - channel->user_count--; - silc_free(chl2); - } - silc_hash_table_list_reset(&htl2); - continue; - } - - /* Remove the channel entry */ - if (silc_idlist_del_channel(server->local_list, channel)) - server->stat.my_channels--; - else - silc_idlist_del_channel(server->global_list, channel); + silc_schedule_task_del_by_context(server->schedule, channel->rekey); + silc_server_channel_delete(server, channel); + silc_buffer_free(clidp); continue; } - /* Send notify to channel about client leaving SILC and thus - the entire channel. */ + /* Send notify to channel about client leaving SILC and channel too */ if (notify) silc_server_send_notify_to_channel(server, NULL, channel, FALSE, SILC_NOTIFY_TYPE_SIGNOFF, @@ -2831,10 +2803,10 @@ void silc_server_remove_from_channels(SilcServer server, signoff_message, signoff_message ? strlen(signoff_message) : 0); + /* Re-generate channel key if needed */ if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { - /* Re-generate channel key */ if (!silc_server_create_channel_key(server, channel, 0)) - goto out; + continue; /* Send the channel key to the channel. The key of course is not sent to the client who was removed from the channel. */ @@ -2844,7 +2816,6 @@ void silc_server_remove_from_channels(SilcServer server, } } - out: silc_hash_table_list_reset(&htl); silc_buffer_free(clidp); } @@ -2855,44 +2826,33 @@ void silc_server_remove_from_channels(SilcServer server, last client leaves the channel. If `notify' is FALSE notify messages are not sent. */ -int silc_server_remove_from_one_channel(SilcServer server, - SilcSocketConnection sock, - SilcChannelEntry channel, - SilcClientEntry client, - int notify) +bool silc_server_remove_from_one_channel(SilcServer server, + SilcSocketConnection sock, + SilcChannelEntry channel, + SilcClientEntry client, + bool notify) { SilcChannelClientEntry chl; SilcBuffer clidp; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Removing %s from channel %s", + silc_id_render(client->id, SILC_ID_CLIENT), + channel->channel_name)); /* Get the entry to the channel, if this client is not on the channel then return Ok. */ if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl)) return TRUE; - /* Remove the client from the channel. The client is removed from - the channel's user list. */ - - clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); - - /* Remove channel from client's channel list */ - silc_hash_table_del(client->channels, chl->channel); - - /* Remove channel if there is no users anymore */ + /* Remove channel if this is last client leaving the channel, unless + the channel is permanent. */ if (server->server_type == SILC_ROUTER && silc_hash_table_count(channel->user_list) < 2) { - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - if (silc_idlist_del_channel(server->local_list, channel)) - server->stat.my_channels--; - else - silc_idlist_del_channel(server->global_list, channel); - silc_buffer_free(clidp); + silc_server_channel_delete(server, channel); return FALSE; } - /* Remove client from channel's client list */ + silc_hash_table_del(client->channels, chl->channel); silc_hash_table_del(channel->user_list, chl->client); channel->user_count--; @@ -2905,8 +2865,13 @@ int silc_server_remove_from_one_channel(SilcServer server, silc_free(chl); server->stat.my_chanclients--; + clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); + if (!clidp) + notify = FALSE; + /* If there is not at least one local user on the channel then we don't - need the channel entry anymore, we can remove it safely. */ + need the channel entry anymore, we can remove it safely, unless the + channel is permanent channel */ if (server->server_type != SILC_ROUTER && !silc_server_channel_has_local(channel)) { /* Notify about leaving client if this channel has global users. */ @@ -2915,34 +2880,9 @@ int silc_server_remove_from_one_channel(SilcServer server, SILC_NOTIFY_TYPE_LEAVE, 1, clidp->data, clidp->len); + silc_schedule_task_del_by_context(server->schedule, channel->rekey); + silc_server_channel_delete(server, channel); silc_buffer_free(clidp); - - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - - if (channel->founder_key) { - /* The founder auth data exists, do not remove the channel entry */ - SilcChannelClientEntry chl2; - SilcHashTableList htl2; - - channel->disabled = TRUE; - - silc_hash_table_list(channel->user_list, &htl2); - while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) { - silc_hash_table_del(chl2->client->channels, channel); - silc_hash_table_del(channel->user_list, chl2->client); - channel->user_count--; - silc_free(chl2); - } - silc_hash_table_list_reset(&htl2); - return FALSE; - } - - /* Remove the channel entry */ - if (silc_idlist_del_channel(server->local_list, channel)) - server->stat.my_channels--; - else - silc_idlist_del_channel(server->global_list, channel); return FALSE; } diff --git a/apps/silcd/server.h b/apps/silcd/server.h index c9659676..404696ce 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -139,14 +139,14 @@ void silc_server_free_sock_user_data(SilcServer server, void silc_server_remove_from_channels(SilcServer server, SilcSocketConnection sock, SilcClientEntry client, - int notify, - char *signoff_message, - int keygen); -int silc_server_remove_from_one_channel(SilcServer server, - SilcSocketConnection sock, - SilcChannelEntry channel, - SilcClientEntry client, - int notify); + bool notify, + const char *signoff_message, + bool keygen); +bool silc_server_remove_from_one_channel(SilcServer server, + SilcSocketConnection sock, + SilcChannelEntry channel, + SilcClientEntry client, + bool notify); void silc_server_disconnect_remote(SilcServer server, SilcSocketConnection sock, SilcStatus status, ...); diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index 38a7c49c..2a7cb7a7 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -51,27 +51,18 @@ static void silc_server_remove_clients_channels(SilcServer server, while (silc_hash_table_get(&htl, NULL, (void **)&chl)) { channel = chl->channel; - /* Remove channel from client's channel list */ - silc_hash_table_del(client->channels, channel); - - /* Remove channel if there is no users anymore */ + /* Remove channel if this is last client leaving the channel, unless + the channel is permanent. */ if (server->server_type == SILC_ROUTER && silc_hash_table_count(channel->user_list) < 2) { - if (silc_hash_table_find(channels, channel, NULL, NULL)) silc_hash_table_del(channels, channel); - - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - - if (silc_idlist_del_channel(server->local_list, channel)) - server->stat.my_channels--; - else - silc_idlist_del_channel(server->global_list, channel); + silc_schedule_task_del_by_context(server->schedule, channel->rekey); + silc_server_channel_delete(server, channel); continue; } - /* Remove client from channel's client list */ + silc_hash_table_del(client->channels, channel); silc_hash_table_del(channel->user_list, chl->client); channel->user_count--; @@ -85,39 +76,14 @@ static void silc_server_remove_clients_channels(SilcServer server, server->stat.my_chanclients--; /* If there is not at least one local user on the channel then we don't - need the channel entry anymore, we can remove it safely. */ + need the channel entry anymore, we can remove it safely, unless the + channel is permanent channel */ if (server->server_type != SILC_ROUTER && !silc_server_channel_has_local(channel)) { - if (silc_hash_table_find(channels, channel, NULL, NULL)) silc_hash_table_del(channels, channel); - - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - - if (channel->founder_key) { - /* The founder auth data exists, do not remove the channel entry */ - SilcChannelClientEntry chl2; - SilcHashTableList htl2; - - channel->disabled = TRUE; - - silc_hash_table_list(channel->user_list, &htl2); - while (silc_hash_table_get(&htl2, NULL, (void **)&chl2)) { - silc_hash_table_del(chl2->client->channels, channel); - silc_hash_table_del(channel->user_list, chl2->client); - channel->user_count--; - silc_free(chl2); - } - silc_hash_table_list_reset(&htl2); - continue; - } - - /* Remove the channel entry */ - if (silc_idlist_del_channel(server->local_list, channel)) - server->stat.my_channels--; - else - silc_idlist_del_channel(server->global_list, channel); + silc_schedule_task_del_by_context(server->schedule, channel->rekey); + silc_server_channel_delete(server, channel); continue; } @@ -127,7 +93,6 @@ static void silc_server_remove_clients_channels(SilcServer server, silc_hash_table_add(channels, channel, channel); } silc_hash_table_list_reset(&htl); - silc_buffer_free(clidp); } @@ -742,6 +707,47 @@ bool silc_server_channel_has_local(SilcChannelEntry channel) return FALSE; } +/* This function removes the channel and all users on the channel, unless + the channel is permanent. In this case the channel is disabled but all + users are removed from the channel. Returns TRUE if the channel is + destroyed totally, and FALSE if it is permanent and remains. */ + +bool silc_server_channel_delete(SilcServer server, + SilcChannelEntry channel) +{ + SilcChannelClientEntry chl; + SilcHashTableList htl; + bool delchan = !(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH); + + if (delchan) { + SILC_LOG_DEBUG(("Deleting %s channel", channel->channel_name)); + + /* Totally delete the channel and all users on the channel. The + users are deleted automatically in silc_idlist_del_channel. */ + silc_schedule_task_del_by_context(server->schedule, channel->rekey); + if (silc_idlist_del_channel(server->local_list, channel)) + server->stat.my_channels--; + else + silc_idlist_del_channel(server->global_list, channel); + return FALSE; + } + + /* Channel is permanent, do not remove it, remove only users */ + channel->disabled = TRUE; + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chl)) { + silc_hash_table_del(chl->client->channels, channel); + silc_hash_table_del(channel->user_list, chl->client); + channel->user_count--; + silc_free(chl); + } + silc_hash_table_list_reset(&htl); + + SILC_LOG_DEBUG(("Channel %s remains", channel->channel_name)); + + return TRUE; +} + /* Returns TRUE if the given client is on the channel. FALSE if not. This works because we assure that the user list on the channel is always in up to date thus we can only check the channel list from @@ -1371,3 +1377,31 @@ bool silc_server_del_from_watcher_list(SilcServer server, return found; } + +/* Force the client indicated by `chl' to change the channel user mode + on channel indicated by `channel' to `forced_mode'. */ + +bool silc_server_force_cumode_change(SilcServer server, + SilcSocketConnection sock, + SilcChannelEntry channel, + SilcChannelClientEntry chl, + SilcUInt32 forced_mode) +{ + SilcBuffer idp1, idp2; + unsigned char cumode[4]; + + silc_server_send_notify_cumode(server, sock, FALSE, channel, forced_mode, + server->id, SILC_ID_SERVER, + chl->client->id); + + idp1 = silc_id_payload_encode(server->id, SILC_ID_SERVER); + idp2 = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT); + SILC_PUT32_MSB(forced_mode, cumode); + silc_server_send_notify_to_channel(server, sock, channel, FALSE, + SILC_NOTIFY_TYPE_CUMODE_CHANGE, + 3, idp1->data, idp1->len, + cumode, sizeof(cumode), + idp2->data, idp2->len); + silc_buffer_free(idp1); + silc_buffer_free(idp2); +} diff --git a/apps/silcd/server_util.h b/apps/silcd/server_util.h index 6b421f23..84ef0783 100644 --- a/apps/silcd/server_util.h +++ b/apps/silcd/server_util.h @@ -67,6 +67,13 @@ bool silc_server_channel_has_global(SilcChannelEntry channel); returns TRUE and FALSE if there is not one locally connected client. */ bool silc_server_channel_has_local(SilcChannelEntry channel); +/* This function removes the channel and all users on the channel, unless + the channel is permanent. In this case the channel is disabled but all + users are removed from the channel. Returns TRUE if the channel is + destroyed totally, and FALSE if it is permanent and remains. */ +bool silc_server_channel_delete(SilcServer server, + SilcChannelEntry channel); + /* Returns TRUE if the given client is on the channel. FALSE if not. This works because we assure that the user list on the channel is always in up to date thus we can only check the channel list from @@ -162,4 +169,12 @@ bool silc_server_check_watcher_list(SilcServer server, bool silc_server_del_from_watcher_list(SilcServer server, SilcClientEntry client); +/* Force the client indicated by `chl' to change the channel user mode + on channel indicated by `channel' to `forced_mode'. */ +bool silc_server_force_cumode_change(SilcServer server, + SilcSocketConnection sock, + SilcChannelEntry channel, + SilcChannelClientEntry chl, + SilcUInt32 forced_mode); + #endif /* SERVER_UTIL_H */ diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index b05f81b6..0d0aeec2 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -1350,7 +1350,6 @@ void silc_client_close_connection_real(SilcClient client, /* Unregister all tasks */ silc_schedule_task_del_by_fd(client->schedule, sock->sock); - silc_schedule_task_del_by_fd(client->schedule, sock->sock); /* Close the actual connection */ silc_net_close_connection(sock->sock); diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index b272e242..dea2e513 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -312,7 +312,7 @@ void silc_client_notify_by_server(SilcClient client, res->context = client; res->sock = silc_socket_dup(conn->sock); res->packet = silc_id_dup(client_id, SILC_ID_CLIENT); - silc_schedule_task_add(client->schedule, 0, + silc_schedule_task_add(client->schedule, conn->sock->sock, silc_client_notify_check_client, res, (5 + (silc_rng_get_rn16(client->rng) % 29)), 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); @@ -898,7 +898,7 @@ void silc_client_notify_by_server(SilcClient client, res->context = client; res->sock = silc_socket_dup(conn->sock); res->packet = silc_id_dup(client_entry->id, SILC_ID_CLIENT); - silc_schedule_task_add(client->schedule, 0, + silc_schedule_task_add(client->schedule, conn->sock->sock, silc_client_notify_check_client, res, (5 + (silc_rng_get_rn16(client->rng) % 529)), 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 9373396e..b62a72fe 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -1413,26 +1413,12 @@ SILC_CLIENT_CMD_FUNC(cmode) if (add) { mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH; type = 7; - - if (cmd->argc < 4) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Usage: /CMODE +|- [{ }]"); - COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); - goto out; - } - - if (!strcasecmp(cmd->argv[3], "-pubkey")) { - auth = silc_auth_public_key_auth_generate(cmd->client->public_key, - cmd->client->private_key, - cmd->client->rng, - conn->hash, - conn->local_id, - SILC_ID_CLIENT); - } else { - auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0, - cmd->argv[3], cmd->argv_lens[3]); - } - + auth = silc_auth_public_key_auth_generate(cmd->client->public_key, + cmd->client->private_key, + cmd->client->rng, + conn->hash, + conn->local_id, + SILC_ID_CLIENT); arg = auth->data; arg_len = auth->len; } else { diff --git a/lib/silccrypt/silcpkcs.c b/lib/silccrypt/silcpkcs.c index 2f2c1d16..8aa9d516 100644 --- a/lib/silccrypt/silcpkcs.c +++ b/lib/silccrypt/silcpkcs.c @@ -811,6 +811,25 @@ bool silc_pkcs_public_key_compare(SilcPublicKey key1, SilcPublicKey key2) return FALSE; } +/* Copies the public key indicated by `public_key' and returns new allocated + public key which is indentical to the `public_key'. */ + +SilcPublicKey silc_pkcs_public_key_copy(SilcPublicKey public_key) +{ + SilcPublicKey key = silc_calloc(1, sizeof(*key)); + if (!key) + return NULL; + + key->len = public_key->len; + key->name = silc_memdup(public_key->name, strlen(public_key->name)); + key->identifier = silc_memdup(public_key->identifier, + strlen(public_key->identifier)); + key->pk = silc_memdup(public_key->pk, public_key->pk_len); + key->pk_len = public_key->pk_len; + + return key; +} + /* Encodes SILC private key from SilcPrivateKey. Returns the encoded data. */ unsigned char * diff --git a/lib/silccrypt/silcpkcs.h b/lib/silccrypt/silcpkcs.h index 8e9ad2e1..86bab733 100644 --- a/lib/silccrypt/silcpkcs.h +++ b/lib/silccrypt/silcpkcs.h @@ -227,6 +227,7 @@ silc_pkcs_public_key_data_encode(unsigned char *pk, SilcUInt32 pk_len, int silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 data_len, SilcPublicKey *public_key); bool silc_pkcs_public_key_compare(SilcPublicKey key1, SilcPublicKey key2); +SilcPublicKey silc_pkcs_public_key_copy(SilcPublicKey public_key); unsigned char * silc_pkcs_private_key_encode(SilcPrivateKey private_key, SilcUInt32 *len); unsigned char *