+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+ /* Cipher mode is unset. Remove the cipher and revert back to
+ default cipher */
+ SilcCipher send_key, receive_key, olds, oldr;
+ cipher = channel->cipher;
+
+ /* Delete old cipher and allocate default one */
+ if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER,
+ &send_key)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, cipher, strlen(cipher));
+ goto out;
+ }
+ if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER,
+ &receive_key)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, cipher, strlen(cipher));
+ goto out;
+ }
+
+ olds = channel->send_key;
+ oldr = channel->receive_key;
+ channel->send_key = send_key;
+ channel->receive_key = receive_key;
+
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0)) {
+ /* We don't have new key, revert to old one */
+ channel->send_key = olds;
+ channel->receive_key = oldr;
+ goto out;
+ }
+
+ /* Remove old channel key for good */
+ silc_cipher_free(olds);
+ silc_cipher_free(oldr);
+
+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
+ /* HMAC to use protect the traffic */
+ unsigned char hash[SILC_HASH_MAXLEN];
+ SilcHmac newhmac;
+
+ /* Get hmac */
+ hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
+ if (!hmac) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
+ }
+
+ /* Delete old hmac and allocate the new one */
+ if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, hmac, strlen(hmac));
+ goto out;
+ }
+
+ silc_hmac_free(channel->hmac);
+ channel->hmac = newhmac;
+
+ /* Set the HMAC key out of current channel key. The client must do
+ this locally. */
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+ channel->key_len / 8, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+ memset(hash, 0, sizeof(hash));
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
+ /* Hmac mode is unset. Remove the hmac and revert back to
+ default hmac */
+ SilcHmac newhmac;
+ unsigned char hash[SILC_HASH_MAXLEN];
+ hmac = channel->hmac_name;
+
+ /* Delete old hmac and allocate default one */
+ if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, hmac, strlen(hmac));
+ goto out;
+ }
+
+ silc_hmac_free(channel->hmac);
+ channel->hmac = newhmac;
+
+ /* Set the HMAC key out of current channel key. The client must do
+ this locally. */
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+ channel->key_len / 8,
+ hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+ memset(hash, 0, sizeof(hash));
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ /* Check if the founder public key was received */
+ founder_key = idata->public_key;
+ tmp = silc_argument_get_arg_type(cmd->args, 8, &tmp_len);
+ if (tmp) {
+ if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ goto out;
+ }
+ } else {
+ /* If key was not sent and the channel mode has already founder
+ then the key was not to be changed. */
+ if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
+ goto has_founder;
+ }
+
+ /* Set the founder authentication */
+ tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
+ }
+
+ /* Verify the payload before setting the mode */
+ if (!silc_auth_verify_data(tmp, tmp_len, SILC_AUTH_PUBLIC_KEY,
+ founder_key, 0, server->sha1hash,
+ client->id, SILC_ID_CLIENT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ goto out;
+ }
+
+ /* Save the public key */
+ if (channel->founder_key)
+ silc_pkcs_public_key_free(channel->founder_key);
+ if (silc_argument_get_arg_type(cmd->args, 8, NULL))
+ channel->founder_key = founder_key;
+ else
+ channel->founder_key = silc_pkcs_public_key_copy(founder_key);
+ if (!channel->founder_key) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ goto out;
+ }
+
+ fkey = silc_public_key_payload_encode(channel->founder_key);
+ if (!fkey) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ silc_pkcs_public_key_free(channel->founder_key);
+ channel->founder_key = NULL;
+ goto out;
+ }
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (channel->founder_key)
+ silc_pkcs_public_key_free(channel->founder_key);
+ channel->founder_key = NULL;
+ }
+ }
+ }
+ has_founder:
+
+ if (mode_mask & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ SilcStatus st;
+
+ chpkdata = silc_argument_get_arg_type(cmd->args, 9, &chpklen);
+
+ if (!chpkdata && channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
+ goto has_pk_list;
+
+ set_chpk = TRUE;
+
+ /* Process the channel public key(s) */
+ st = silc_server_set_channel_pk_list(server, NULL, channel,
+ chpkdata, chpklen);
+ if (st != SILC_STATUS_OK) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, st, 0);
+ goto out;
+ }
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+ if (channel->channel_pubkeys)
+ silc_hash_table_free(channel->channel_pubkeys);
+ channel->channel_pubkeys = NULL;
+ set_chpk = TRUE;
+ }
+ }
+ }
+ has_pk_list:
+
+ /* Finally, set the mode */
+ old_mask = channel->mode = mode_mask;
+
+ /* Send CMODE_CHANGE notify. */
+ cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (mode_mask & SILC_CHANNEL_MODE_ULIMIT)
+ SILC_PUT32_MSB(channel->user_limit, ulimit);
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_CMODE_CHANGE, 8,
+ cidp->data, silc_buffer_len(cidp),
+ tmp_mask, 4,
+ cipher, cipher ? strlen(cipher) : 0,
+ hmac, hmac ? strlen(hmac) : 0,
+ passphrase, passphrase ?
+ strlen(passphrase) : 0,
+ fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0,
+ chpkdata ? chpkdata : NULL,
+ chpkdata ? chpklen : 0,
+ mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL,
+ mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 0);
+
+ /* Set CMODE notify type to network */
+ if (chpkdata && chpklen)
+ silc_buffer_set(&chpk, chpkdata, chpklen);
+ silc_server_send_notify_cmode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel,
+ mode_mask, client->id, SILC_ID_CLIENT,
+ cipher, hmac, passphrase, founder_key,
+ chpkdata ? &chpk : NULL);
+
+ if (set_chpk)
+ chpklist = silc_server_get_channel_pk_list(server, channel, FALSE, FALSE);
+
+ /* Send command reply to sender */
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_CMODE,
+ SILC_STATUS_OK, 0, ident, 5,
+ 2, tmp_id, tmp_len2,
+ 3, tmp_mask, 4,
+ 4, fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0,
+ 5, chpklist ? chpklist->data :
+ NULL, chpklist ? silc_buffer_len(chpklist)
+ : 0,
+ 6, (mode_mask &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL),
+ (mode_mask &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 0));
+ silc_buffer_free(cidp);
+
+ out:
+ channel->mode = old_mask;
+ silc_buffer_free(chpklist);
+ silc_buffer_free(fkey);
+ silc_server_command_free(cmd);
+}
+
+/* Server side of CUMODE command. Changes client's mode on a channel. */
+
+SILC_SERVER_CMD_FUNC(cumode)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ SilcID id, id2;
+ SilcChannelEntry channel;
+ SilcClientEntry target_client;
+ SilcChannelClientEntry chl;
+ SilcBuffer idp;
+ unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
+ SilcUInt32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
+ int notify = FALSE;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ SilcPublicKey founder_key = NULL;
+ SilcBuffer fkey = NULL;
+
+ if (!client)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
+
+ /* Get Channel ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);