X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fcommand.c;h=5d94f74b9a02c69d38ed45df0c1b16dc8daabf0c;hp=0d594b9d9891049af8823d33408054cd9316dc73;hb=c257b555225193e54d85daf541d29578b3c93882;hpb=f658940d02cf2fd893296b6a7825b42502573668 diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 0d594b9d..5d94f74b 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2003 Pekka Riikonen + Copyright (C) 1997 - 2005 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -206,7 +206,12 @@ void silc_server_command_process(SilcServer server, ctx->payload = silc_command_payload_parse(packet->buffer->data, packet->buffer->len); if (!ctx->payload) { - SILC_LOG_ERROR(("Bad command payload, packet dropped")); + SILC_LOG_ERROR(("Bad command payload, dropped (%s:%d [%s])", + sock->hostname, sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); silc_packet_context_free(packet); silc_socket_free(ctx->sock); silc_free(ctx); @@ -621,48 +626,54 @@ SILC_SERVER_CMD_FUNC(nick) SilcBuffer packet, nidp, oidp = NULL; SilcClientID *new_id; SilcUInt32 nick_len; - char *nick; + unsigned char *nick, *nickc = NULL; SilcUInt16 ident = silc_command_get_ident(cmd->payload); - int nickfail = 0; if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client) goto out; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1); - /* Check nickname */ + /* Get nickname */ nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len); if (!nick) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, SILC_STATUS_ERR_BAD_NICKNAME, 0); goto out; } - if (nick_len > 128) + + /* Truncate over long nicks */ + if (nick_len > 128) { nick[128] = '\0'; - if (silc_server_name_bad_chars(nick, nick_len) == TRUE) { + nick_len = 128; + } + + /* Check for valid nickname string. This is cached, original is saved + in the client context. */ + nickc = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, NULL); + if (!nickc) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, SILC_STATUS_ERR_BAD_NICKNAME, 0); goto out; } /* Check for same nickname */ - if (!strcmp(client->nickname, nick)) { + if (strlen(client->nickname) == nick_len && + !memcmp(client->nickname, nick, nick_len)) { nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); + silc_free(nickc); goto send_reply; } /* Create new Client ID */ - while (!silc_id_create_client_id(cmd->server, cmd->server->id, - cmd->server->rng, - cmd->server->md5hash, nick, - &new_id)) { - nickfail++; - if (nickfail > 9) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, - SILC_STATUS_ERR_BAD_NICKNAME, 0); - goto out; - } - snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail); + if (!silc_id_create_client_id(cmd->server, cmd->server->id, + cmd->server->rng, + cmd->server->md5hash, + nickc, strlen(nickc), &new_id)) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_free(nickc); + goto out; } /* Send notify about nickname change to our router. We send the new @@ -689,7 +700,7 @@ SILC_SERVER_CMD_FUNC(nick) client->nickname = strdup(nick); /* Update client cache */ - silc_idcache_add(server->local_list->clients, client->nickname, + silc_idcache_add(server->local_list->clients, nickc, client->id, (void *)client, 0, NULL); nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); @@ -712,7 +723,7 @@ SILC_SERVER_CMD_FUNC(nick) packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, SILC_STATUS_OK, 0, ident, 2, 2, nidp->data, nidp->len, - 3, nick, strlen(nick)); + 3, nick, nick_len); silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); @@ -757,6 +768,12 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd, valid_rcount++; } + if (!lch_count && !gch_count) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST, + SILC_STATUS_OK, 0); + return; + } + status = SILC_STATUS_OK; if ((lch_count + gch_count) > 1) status = SILC_STATUS_LIST_START; @@ -978,6 +995,13 @@ SILC_SERVER_CMD_FUNC(topic) goto out; } + if (!silc_utf8_valid(tmp, strlen(tmp))) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, + 0); + goto out; + } + /* See whether the client is on channel and has rights to change topic */ if (!silc_server_client_on_channel(client, channel, &chl)) { tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len); @@ -1174,7 +1198,7 @@ SILC_SERVER_CMD_FUNC(invite) /* Check if the ID is in the list already */ silc_hash_table_list(channel->invite_list, &htl); - while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) { + while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2)) { if (type == 3 && !memcmp(tmp2->data, tmp, len)) { tmp = NULL; break; @@ -1241,8 +1265,14 @@ SILC_SERVER_CMD_FUNC(invite) } /* Now add or delete the information. */ - silc_server_inviteban_process(server, channel->invite_list, - (SilcUInt8)atype[0], args); + if (!silc_server_inviteban_process(server, channel->invite_list, + (SilcUInt8)atype[0], args)) { + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_INVITE, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, + 0); + goto out; + } } silc_argument_payload_free(args); } @@ -1256,7 +1286,7 @@ SILC_SERVER_CMD_FUNC(invite) channel->invite_list)), SILC_STR_END); silc_hash_table_list(channel->invite_list, &htl); - while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) + while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2)) list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len, type); silc_hash_table_list_reset(&htl); @@ -1332,14 +1362,17 @@ SILC_TASK_CALLBACK(silc_server_command_quit_cb) SilcServer server = app_context; QuitInternal q = (QuitInternal)context; - /* Free all client specific data, such as client entry and entires - on channels this client may be on. */ - silc_server_free_client_data(server, q->sock, q->sock->user_data, - TRUE, q->signoff); - q->sock->user_data = NULL; + if (q->sock->user_data) { + /* Free all client specific data, such as client entry and entires + on channels this client may be on. */ + silc_server_free_client_data(server, q->sock, q->sock->user_data, + TRUE, q->signoff); + q->sock->user_data = NULL; + } - /* Close the connection on our side */ - silc_server_close_connection(server, q->sock); + if (!SILC_IS_DISCONNECTED(q->sock)) + /* Close the connection on our side */ + silc_server_close_connection(server, q->sock); silc_socket_free(q->sock); silc_free(q->signoff); @@ -1526,7 +1559,7 @@ SILC_SERVER_CMD_FUNC(info) SilcBuffer packet, idp; unsigned char *tmp; SilcUInt32 tmp_len; - char *dest_server, *server_info = NULL, *server_name; + char *dest_server = NULL, *server_info = NULL, *server_name; SilcUInt16 ident = silc_command_get_ident(cmd->payload); SilcServerEntry entry = NULL; SilcServerID *server_id = NULL; @@ -1535,6 +1568,16 @@ SILC_SERVER_CMD_FUNC(info) /* Get server name */ dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL); + if (dest_server) { + /* Check server name. */ + dest_server = silc_identifier_check(dest_server, strlen(dest_server), + SILC_STRING_UTF8, 256, &tmp_len); + if (!dest_server) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO, + SILC_STATUS_ERR_BAD_SERVER, 0); + goto out; + } + } /* Get Server ID */ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); @@ -1570,7 +1613,7 @@ SILC_SERVER_CMD_FUNC(info) if ((!dest_server && !server_id && !entry) || (entry && entry == server->id_entry) || (dest_server && !cmd->pending && - !strncasecmp(dest_server, server->server_name, strlen(dest_server)))) { + !memcmp(dest_server, server->server_name, strlen(dest_server)))) { /* Send our reply */ char info_string[256]; @@ -1648,11 +1691,15 @@ SILC_SERVER_CMD_FUNC(info) silc_free(server_id); if (!entry) { - if (dest_server) + if (dest_server) { + silc_free(dest_server); + dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL); silc_server_command_send_status_data(cmd, SILC_COMMAND_INFO, SILC_STATUS_ERR_NO_SUCH_SERVER, 0, 2, dest_server, strlen(dest_server)); + dest_server = NULL; + } goto out; } @@ -1677,6 +1724,7 @@ SILC_SERVER_CMD_FUNC(info) silc_buffer_free(idp); out: + silc_free(dest_server); silc_server_command_free(cmd); } @@ -1836,7 +1884,7 @@ static void silc_server_command_join_channel(SilcServer server, SilcSocketConnection sock = cmd->sock; unsigned char *tmp; SilcUInt32 tmp_len, user_count; - unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4]; + unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4], ulimit[4]; SilcClientEntry client; SilcChannelClientEntry chl; SilcBuffer reply, chidp, clidp, keyp = NULL; @@ -1863,9 +1911,11 @@ static void silc_server_command_join_channel(SilcServer server, &resolve); if (!client) { if (!resolve || cmd->pending) { - silc_server_command_send_status_reply( + tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + silc_server_command_send_status_data( cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0, + 2, tmp, tmp_len); goto out; } @@ -1962,7 +2012,10 @@ static void silc_server_command_join_channel(SilcServer server, if (!strchr(client->nickname, '@')) { silc_strncat(check2, sizeof(check2), "@", 1); silc_strncat(check2, sizeof(check2), - server->server_name, strlen(server->server_name)); + SILC_IS_LOCAL(client) ? server->server_name : + client->router->server_name, + SILC_IS_LOCAL(client) ? strlen(server->server_name) : + strlen(client->router->server_name)); } silc_strncat(check2, sizeof(check2), "!", 1); silc_strncat(check2, sizeof(check2), @@ -2042,6 +2095,7 @@ static void silc_server_command_join_channel(SilcServer server, passphrase = silc_memdup(tmp, tmp_len); if (!passphrase || !channel->passphrase || + strlen(channel->passphrase) != strlen(passphrase) || memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) { chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN, @@ -2116,6 +2170,8 @@ static void silc_server_command_join_channel(SilcServer server, SILC_PUT32_MSB(channel->mode, mode); SILC_PUT32_MSB(created, tmp2); SILC_PUT32_MSB(user_count, tmp3); + if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) + SILC_PUT32_MSB(channel->user_limit, ulimit); if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL); @@ -2143,7 +2199,7 @@ static void silc_server_command_join_channel(SilcServer server, SILC_STR_END); silc_hash_table_list(channel->invite_list, &htl); - while (silc_hash_table_get(&htl, (void **)&tmp_len, (void **)&reply)) + while (silc_hash_table_get(&htl, (void *)&tmp_len, (void *)&reply)) invite_list = silc_argument_payload_encode_one(invite_list, reply->data, reply->len, tmp_len); @@ -2162,7 +2218,7 @@ static void silc_server_command_join_channel(SilcServer server, SILC_STR_END); silc_hash_table_list(channel->ban_list, &htl); - while (silc_hash_table_get(&htl, (void **)&tmp_len, (void **)&reply)) + while (silc_hash_table_get(&htl, (void *)&tmp_len, (void *)&reply)) ban_list = silc_argument_payload_encode_one(ban_list, reply->data, reply->len, tmp_len); @@ -2174,7 +2230,7 @@ static void silc_server_command_join_channel(SilcServer server, reply = silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN, - SILC_STATUS_OK, 0, ident, 15, + SILC_STATUS_OK, 0, ident, 16, 2, channel->channel_name, strlen(channel->channel_name), 3, chidp->data, chidp->len, @@ -2201,7 +2257,13 @@ static void silc_server_command_join_channel(SilcServer server, 15, fkey ? fkey->data : NULL, fkey ? fkey->len : 0, 16, chpklist ? chpklist->data : NULL, - chpklist ? chpklist->len : 0); + chpklist ? chpklist->len : 0, + 17, (channel->mode & + SILC_CHANNEL_MODE_ULIMIT ? + ulimit : NULL), + (channel->mode & + SILC_CHANNEL_MODE_ULIMIT ? + sizeof(ulimit) : 0)); /* Send command reply */ silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, @@ -2283,7 +2345,7 @@ SILC_SERVER_CMD_FUNC(join) SilcServer server = cmd->server; unsigned char *auth, *cauth; SilcUInt32 tmp_len, auth_len, cauth_len; - char *tmp, *channel_name = NULL, *cipher, *hmac; + char *tmp, *channel_name, *channel_namec = NULL, *cipher, *hmac; SilcChannelEntry channel; SilcUInt32 umode = 0; bool created = FALSE, create_key = TRUE; @@ -2299,12 +2361,19 @@ SILC_SERVER_CMD_FUNC(join) 0); goto out; } - channel_name = tmp; - if (tmp_len > 256) - channel_name[255] = '\0'; + /* Truncate over long channel names */ + if (tmp_len > 256) { + tmp[256] = '\0'; + tmp_len = 256; + } + channel_name = tmp; - if (silc_server_name_bad_chchars(channel_name, tmp_len) == TRUE) { + /* Check for valid channel name. This is cached, the original is saved + in the channel context. */ + channel_namec = silc_channel_name_check(tmp, tmp_len, SILC_STRING_UTF8, 256, + NULL); + if (!channel_namec) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, SILC_STATUS_ERR_BAD_CHANNEL, 0); goto out; @@ -2314,15 +2383,15 @@ SILC_SERVER_CMD_FUNC(join) tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (!tmp) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, + SILC_STATUS_ERR_NO_CLIENT_ID, 0); goto out; } client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); if (!client_id) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, - 0); + silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_BAD_CLIENT_ID, 0, + 2, tmp, tmp_len); goto out; } @@ -2334,7 +2403,7 @@ SILC_SERVER_CMD_FUNC(join) /* See if the channel exists */ channel = silc_idlist_find_channel_by_name(server->local_list, - channel_name, NULL); + channel_namec, NULL); if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) { SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data; @@ -2414,7 +2483,7 @@ SILC_SERVER_CMD_FUNC(join) /* We are router and the channel does not seem exist so we will check our global list as well for the channel. */ channel = silc_idlist_find_channel_by_name(server->global_list, - channel_name, NULL); + channel_namec, NULL); if (!channel) { /* Channel really does not exist, create it */ channel = silc_server_create_new_channel(server, server->id, cipher, @@ -2450,7 +2519,7 @@ SILC_SERVER_CMD_FUNC(join) /* We are router and the channel does not seem exist so we will check our global list as well for the channel. */ channel = silc_idlist_find_channel_by_name(server->global_list, - channel_name, NULL); + channel_namec, NULL); if (!channel) { /* Channel really does not exist, create it */ channel = silc_server_create_new_channel(server, server->id, cipher, @@ -2513,6 +2582,7 @@ SILC_SERVER_CMD_FUNC(join) silc_free(client_id); out: + silc_free(channel_namec); silc_server_command_free(cmd); } @@ -2524,7 +2594,7 @@ SILC_SERVER_CMD_FUNC(motd) SilcServerCommandContext cmd = (SilcServerCommandContext)context; SilcServer server = cmd->server; SilcBuffer packet, idp; - char *motd, *dest_server; + char *motd, *dest_server = NULL; SilcUInt32 motd_len; SilcUInt16 ident = silc_command_get_ident(cmd->payload); @@ -2539,7 +2609,17 @@ SILC_SERVER_CMD_FUNC(motd) goto out; } - if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) { + /* Check server name */ + dest_server = silc_identifier_check(dest_server, strlen(dest_server), + SILC_STRING_UTF8, 256, NULL); + if (!dest_server) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD, + SILC_STATUS_ERR_BAD_SERVER, + 0); + goto out; + } + + if (!memcmp(dest_server, server->server_name, strlen(dest_server))) { /* Send our MOTD */ idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER); @@ -2556,14 +2636,14 @@ SILC_SERVER_CMD_FUNC(motd) packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD, SILC_STATUS_OK, 0, ident, 2, - 2, idp, idp->len, + 2, idp->data, idp->len, 3, motd, motd_len); } else { /* No motd */ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD, SILC_STATUS_OK, 0, ident, 1, - 2, idp, idp->len); + 2, idp->data, idp->len); } silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, @@ -2606,7 +2686,10 @@ SILC_SERVER_CMD_FUNC(motd) goto out; } - if (!entry && !cmd->pending && !server->standalone) { + /* Send to primary router only if we don't know the server + * the client requested or if the server is not locally connected */ + if ((!entry || !(entry->data.status & SILC_IDLIST_STATUS_LOCAL)) + && !cmd->pending && !server->standalone) { /* Send to the primary router */ SilcBuffer tmpbuf; SilcUInt16 old_ident; @@ -2631,17 +2714,20 @@ SILC_SERVER_CMD_FUNC(motd) } if (!entry) { + silc_free(dest_server); + dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL); silc_server_command_send_status_data(cmd, SILC_COMMAND_MOTD, SILC_STATUS_ERR_NO_SUCH_SERVER, 0, 2, dest_server, strlen(dest_server)); + dest_server = NULL; goto out; } - idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER); + idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER); packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD, SILC_STATUS_OK, 0, ident, 2, - 2, idp, idp->len, + 2, idp->data, idp->len, 3, entry->motd, entry->motd ? strlen(entry->motd) : 0); @@ -2652,6 +2738,7 @@ SILC_SERVER_CMD_FUNC(motd) } out: + silc_free(dest_server); silc_server_command_free(cmd); } @@ -2691,18 +2778,11 @@ SILC_SERVER_CMD_FUNC(umode) } /* Anonymous mode cannot be set by client */ - if (mask & SILC_UMODE_ANONYMOUS) { - if (!(client->mode & SILC_UMODE_ANONYMOUS)) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, - SILC_STATUS_ERR_PERM_DENIED, 0); - goto out; - } - } else { - if (client->mode & SILC_UMODE_ANONYMOUS) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, - SILC_STATUS_ERR_PERM_DENIED, 0); - goto out; - } + if (mask & SILC_UMODE_ANONYMOUS && + !(client->mode & SILC_UMODE_ANONYMOUS)) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE, + SILC_STATUS_ERR_PERM_DENIED, 0); + goto out; } /* Update statistics */ @@ -2714,6 +2794,10 @@ SILC_SERVER_CMD_FUNC(umode) server->stat.my_aways--; } + /* If the client has anonymous mode set, preserve it. */ + if (client->mode & SILC_UMODE_ANONYMOUS) + mask |= SILC_UMODE_ANONYMOUS; + /* Change the mode */ client->mode = mask; @@ -2754,7 +2838,7 @@ SILC_SERVER_CMD_FUNC(cmode) SilcChannelClientEntry chl; SilcBuffer packet, cidp; unsigned char *tmp, *tmp_id, *tmp_mask, *chpkdata = NULL; - char *cipher = NULL, *hmac = NULL, *passphrase = NULL; + char *cipher = NULL, *hmac = NULL, *passphrase = NULL, ulimit[4]; SilcUInt32 mode_mask = 0, old_mask = 0, tmp_len, tmp_len2, chpklen; SilcUInt16 ident = silc_command_get_ident(cmd->payload); bool set_mask = FALSE, set_chpk = FALSE; @@ -3134,7 +3218,6 @@ SILC_SERVER_CMD_FUNC(cmode) channel->founder_key = NULL; goto out; } - has_founder: } } else { if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) { @@ -3145,6 +3228,7 @@ SILC_SERVER_CMD_FUNC(cmode) } } } + has_founder: if (mode_mask & SILC_CHANNEL_MODE_CHANNEL_AUTH) { if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) { @@ -3164,7 +3248,6 @@ SILC_SERVER_CMD_FUNC(cmode) silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, st, 0); goto out; } - has_pk_list: } } else { if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) { @@ -3176,14 +3259,17 @@ SILC_SERVER_CMD_FUNC(cmode) } } } + 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, 7, + SILC_NOTIFY_TYPE_CMODE_CHANGE, 8, cidp->data, cidp->len, tmp_mask, 4, cipher, cipher ? strlen(cipher) : 0, @@ -3193,7 +3279,11 @@ SILC_SERVER_CMD_FUNC(cmode) fkey ? fkey->data : NULL, fkey ? fkey->len : 0, chpkdata ? chpkdata : NULL, - chpkdata ? chpklen : 0); + 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) @@ -3209,14 +3299,21 @@ SILC_SERVER_CMD_FUNC(cmode) /* Send command reply to sender */ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE, - SILC_STATUS_OK, 0, ident, 4, + SILC_STATUS_OK, 0, ident, 5, 2, tmp_id, tmp_len2, 3, tmp_mask, 4, 4, fkey ? fkey->data : NULL, fkey ? fkey->len : 0, 5, chpklist ? chpklist->data : NULL, chpklist ? chpklist->len - : 0); + : 0, + 6, (mode_mask & + SILC_CHANNEL_MODE_ULIMIT ? + ulimit : NULL), + (mode_mask & + SILC_CHANNEL_MODE_ULIMIT ? + sizeof(ulimit) : 0)); + silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); @@ -3320,16 +3417,15 @@ SILC_SERVER_CMD_FUNC(cumode) /* Get target client's entry */ target_client = silc_idlist_find_client_by_id(server->local_list, client_id, TRUE, NULL); - if (!target_client) { + if (!target_client) target_client = silc_idlist_find_client_by_id(server->global_list, client_id, TRUE, NULL); - } if (target_client != client && !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) && !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) { silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE, - SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0, + SILC_STATUS_ERR_NOT_YOU, 0, 2, tmp_ch_id, tmp_ch_len); goto out; } @@ -3362,7 +3458,8 @@ SILC_SERVER_CMD_FUNC(cumode) if (target_mask & SILC_CHANNEL_UMODE_CHANFO) { if (target_client != client) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE, - SILC_STATUS_ERR_NOT_YOU, 0); + SILC_STATUS_ERR_NO_CHANNEL_FOPRIV, + 0); goto out; } @@ -3406,16 +3503,23 @@ SILC_SERVER_CMD_FUNC(cumode) } /* There cannot be anyone else as founder on the channel now. This - client is definitely the founder due to this authentication */ - silc_hash_table_list(channel->user_list, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chl2)) - if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) { - chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO; - silc_server_force_cumode_change(server, NULL, channel, chl2, - chl2->mode); - break; - } - silc_hash_table_list_reset(&htl); + client is definitely the founder due to this authentication. This + is done only on router, not on server, since server cannot know + whether router will accept this mode change or not. XXX This + probably shouldn't be done anymore at all, may cause problems in + router-router connections too (maybe just AUTH_FAILED error should + be returned). -Pekka */ + if (server->server_type == SILC_ROUTER) { + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chl2)) + if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) { + chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO; + silc_server_force_cumode_change(server, NULL, channel, chl2, + chl2->mode); + break; + } + silc_hash_table_list_reset(&htl); + } sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO; } @@ -3721,10 +3825,6 @@ SILC_SERVER_CMD_FUNC(kick) packet->data, packet->len, FALSE); silc_buffer_free(packet); - /* Send command reply to sender */ - silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, - SILC_STATUS_OK, 0); - /* Send KICKED notify to local clients on the channel */ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE, @@ -3745,6 +3845,7 @@ SILC_SERVER_CMD_FUNC(kick) silc_argument_payload_encode_one(NULL, target_idp, target_idp_len, 3); SilcArgumentPayload args = silc_argument_payload_parse(ab->data, ab->len, 1); + silc_server_inviteban_process(server, channel->invite_list, 1, args); silc_buffer_free(ab); silc_argument_payload_free(args); @@ -3781,7 +3882,7 @@ SILC_SERVER_CMD_FUNC(oper) SilcServerCommandContext cmd = (SilcServerCommandContext)context; SilcServer server = cmd->server; SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data; - unsigned char *username, *auth; + unsigned char *username = NULL, *auth; SilcUInt32 tmp_len; SilcServerConfigAdmin *admin; SilcIDListData idata = (SilcIDListData)client; @@ -3802,6 +3903,16 @@ SILC_SERVER_CMD_FUNC(oper) goto out; } + /* Check username */ + username = silc_identifier_check(username, strlen(username), + SILC_STRING_UTF8, 128, &tmp_len); + if (!username) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER, + SILC_STATUS_ERR_BAD_USERNAME, + 0); + goto out; + } + /* Get the admin configuration */ admin = silc_server_config_find_admin(server, cmd->sock->ip, username, client->nickname); @@ -3874,6 +3985,7 @@ SILC_SERVER_CMD_FUNC(oper) SILC_STATUS_OK, 0); out: + silc_free(username); silc_server_command_free(cmd); } @@ -3996,11 +4108,11 @@ SILC_SERVER_CMD_FUNC(watch) SilcServerCommandContext cmd = (SilcServerCommandContext)context; SilcServer server = cmd->server; char *add_nick, *del_nick; - SilcUInt32 add_nick_len, del_nick_len, tmp_len; - char nick[128 + 1]; - unsigned char hash[16], *tmp; + SilcUInt32 add_nick_len, del_nick_len, tmp_len, pk_len; + unsigned char hash[16], *tmp, *pk, *nick; SilcClientEntry client; SilcClientID *client_id = NULL; + SilcUInt16 old_ident; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WATCH, cmd, 1, 3); @@ -4008,7 +4120,11 @@ SILC_SERVER_CMD_FUNC(watch) if (!cmd->pending) { /* Send the command to router */ SilcBuffer tmpbuf; - SilcUInt16 old_ident; + + /* If backup receives this from primary, handle it locally */ + if (server->server_type == SILC_BACKUP_ROUTER && + cmd->sock == SILC_PRIMARY_ROUTE(server)) + goto process_watch; SILC_LOG_DEBUG(("Forwarding WATCH to router")); @@ -4028,21 +4144,30 @@ SILC_SERVER_CMD_FUNC(watch) cmd->pending = TRUE; silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - } else if (context2) { - /* Received reply from router, just send same data to the client. */ + goto out; + } else { SilcServerCommandReplyContext reply = context2; SilcStatus status; - SILC_LOG_DEBUG(("Received reply to WATCH from router")); + if (!reply) + goto out; + silc_command_get_status(reply->payload, &status, NULL); - silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status, - 0); - } - goto out; + /* Backup router handles the WATCH command also. */ + if (server->server_type != SILC_BACKUP_ROUTER || + SILC_STATUS_IS_ERROR(status)) { + /* Received reply from router, just send same data to the client. */ + SILC_LOG_DEBUG(("Received reply to WATCH from router")); + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status, + 0); + goto out; + } + } } /* We are router and keep the watch list for local cell */ + process_watch: /* Get the client ID */ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len); @@ -4064,32 +4189,45 @@ SILC_SERVER_CMD_FUNC(watch) client = silc_idlist_find_client_by_id(server->local_list, client_id, TRUE, NULL); if (!client) { - silc_server_command_send_status_data(cmd, SILC_COMMAND_WATCH, - SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0, - 2, tmp, tmp_len); - goto out; + /* Backup checks global list also */ + if (server->server_type == SILC_BACKUP_ROUTER) + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!client) { + silc_server_command_send_status_data(cmd, SILC_COMMAND_WATCH, + SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, + 0, 2, tmp, tmp_len); + goto out; + } } + /* Take public key for watching by public key */ + pk = silc_argument_get_arg_type(cmd->args, 4, &pk_len); + /* Take nickname */ add_nick = silc_argument_get_arg_type(cmd->args, 2, &add_nick_len); del_nick = silc_argument_get_arg_type(cmd->args, 3, &del_nick_len); - if (!add_nick && !del_nick) { + if (!add_nick && !del_nick && !pk) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); goto out; } - if (add_nick && add_nick_len > 128) + if (add_nick && add_nick_len > 128) { add_nick[128] = '\0'; - if (del_nick && del_nick_len > 128) + add_nick_len = 128; + } + if (del_nick && del_nick_len > 128) { del_nick[128] = '\0'; - - memset(nick, 0, sizeof(nick)); + del_nick_len = 128; + } /* Add new nickname to be watched in our cell */ if (add_nick) { - if (silc_server_name_bad_chars(add_nick, strlen(add_nick)) == TRUE) { + nick = silc_identifier_check(add_nick, add_nick_len, SILC_STRING_UTF8, 128, + &add_nick_len); + if (!nick) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, SILC_STATUS_ERR_BAD_NICKNAME, 0); goto out; @@ -4097,8 +4235,7 @@ SILC_SERVER_CMD_FUNC(watch) /* Hash the nick, we have the hash saved, not nicks because we can do one to one mapping to the nick from Client ID hash this way. */ - silc_to_lower(add_nick, nick, sizeof(nick) - 1); - silc_hash_make(server->md5hash, nick, strlen(nick), hash); + silc_hash_make(server->md5hash, nick, add_nick_len, hash); /* Check whether this client is already watching this nickname */ if (silc_hash_table_find_by_context(server->watcher_list, hash, @@ -4107,21 +4244,25 @@ SILC_SERVER_CMD_FUNC(watch) silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, SILC_STATUS_ERR_NICKNAME_IN_USE, 0); + silc_free(nick); goto out; } /* Get the nickname from the watcher list and use the same key in new entries as well. If key doesn't exist then create it. */ - if (!silc_hash_table_find(server->watcher_list, hash, (void **)&tmp, NULL)) + if (!silc_hash_table_find(server->watcher_list, hash, (void *)&tmp, NULL)) tmp = silc_memdup(hash, CLIENTID_HASH_LEN); /* Add the client to the watcher list with the specified nickname hash. */ silc_hash_table_add(server->watcher_list, tmp, client); + silc_free(nick); } /* Delete nickname from watch list */ if (del_nick) { - if (silc_server_name_bad_chars(del_nick, strlen(del_nick)) == TRUE) { + nick = silc_identifier_check(del_nick, del_nick_len, SILC_STRING_UTF8, 128, + &del_nick_len); + if (!nick) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, SILC_STATUS_ERR_BAD_NICKNAME, 0); goto out; @@ -4129,16 +4270,16 @@ SILC_SERVER_CMD_FUNC(watch) /* Hash the nick, we have the hash saved, not nicks because we can do one to one mapping to the nick from Client ID hash this way. */ - silc_to_lower(del_nick, nick, sizeof(nick) - 1); - silc_hash_make(server->md5hash, nick, strlen(nick), hash); + silc_hash_make(server->md5hash, nick, del_nick_len, hash); /* Check that this client is watching for this nickname */ if (!silc_hash_table_find_by_context(server->watcher_list, hash, - client, (void **)&tmp)) { + client, (void *)&tmp)) { /* Nickname is alredy being watched for this client */ silc_server_command_send_status_data(cmd, SILC_COMMAND_WATCH, SILC_STATUS_ERR_NO_SUCH_NICK, 0, - 2, nick, strlen(nick)); + 2, nick, del_nick_len); + silc_free(nick); goto out; } @@ -4149,22 +4290,112 @@ SILC_SERVER_CMD_FUNC(watch) then free the key to not leak memory. */ if (!silc_hash_table_find(server->watcher_list, hash, NULL, NULL)) silc_free(tmp); + silc_free(nick); } + /* Add/del public key */ + if (pk) { + SilcUInt16 pkargc; + SilcArgumentPayload pkargs; + SilcUInt32 type; + SilcPublicKey public_key, pkkey; + + if (pk_len < 2) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, + 0); + goto out; + } + + /* Get the argument from the Argument List Payload */ + SILC_GET16_MSB(pkargc, pk); + pkargs = silc_argument_payload_parse(pk + 2, pk_len - 2, pkargc); + if (!pkargs) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, + 0); + goto out; + } + + pk = silc_argument_get_next_arg(pkargs, &type, &pk_len); + while (pk) { + if (!silc_pkcs_public_key_payload_decode(pk, pk_len, &public_key)) + continue; + if (type == 0x03) + type = 0x00; + + if (type == 0x00) { + /* Add public key to watch list */ + + /* Check whether this client is already watching this public key */ + if (silc_hash_table_find_by_context(server->watcher_list_pk, + public_key, client, NULL)) { + silc_pkcs_public_key_free(public_key); + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_WATCH, + SILC_STATUS_ERR_NICKNAME_IN_USE, 0); + goto out; + } + + /* Get the public key from the watcher list and use the same key in + new entries as well. If key doesn't exist then create it. */ + pkkey = NULL; + if (!silc_hash_table_find(server->watcher_list_pk, public_key, + (void *)&pkkey, NULL)) + pkkey = public_key; + else + silc_pkcs_public_key_free(public_key); + + /* Add the client to the watcher list with the specified public + key. */ + silc_hash_table_add(server->watcher_list_pk, pkkey, client); + + } else if (type == 0x01) { + /* Delete public key from watch list */ + + /* Check that this client is watching this public key */ + if (silc_hash_table_find_by_context(server->watcher_list_pk, + public_key, client, + (void *)&pkkey)) { + silc_pkcs_public_key_free(public_key); + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_WATCH, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + goto out; + } + + /* Delete the public key from the watcher list. */ + silc_hash_table_del_by_context(server->watcher_list_pk, + public_key, client); + + /* Now check whether there still exists entries with this key, if + not then free the key to not leak memory. */ + if (!silc_hash_table_find(server->watcher_list_pk, hash, NULL, NULL)) + silc_pkcs_public_key_free(pkkey); + silc_pkcs_public_key_free(public_key); + } + + pk = silc_argument_get_next_arg(pkargs, &type, &pk_len); + } + } + + /* Send reply */ + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, + SILC_STATUS_OK, 0); + /* Distribute the watch list to backup routers too */ if (server->backup) { SilcBuffer tmpbuf; + old_ident = silc_command_get_ident(cmd->payload); silc_command_set_ident(cmd->payload, ++server->cmd_ident); tmpbuf = silc_command_payload_encode_payload(cmd->payload); - silc_server_backup_send(server, NULL, SILC_PACKET_COMMAND, + silc_server_backup_send(server, cmd->sock->user_data, SILC_PACKET_COMMAND, cmd->packet->flags, tmpbuf->data, tmpbuf->len, FALSE, TRUE); + silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); } - silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, - SILC_STATUS_OK, 0); - out: silc_free(client_id); silc_server_command_free(cmd); @@ -4178,7 +4409,7 @@ SILC_SERVER_CMD_FUNC(silcoper) SilcServerCommandContext cmd = (SilcServerCommandContext)context; SilcServer server = cmd->server; SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data; - unsigned char *username, *auth; + unsigned char *username = NULL, *auth; SilcUInt32 tmp_len; SilcServerConfigAdmin *admin; SilcIDListData idata = (SilcIDListData)client; @@ -4205,6 +4436,16 @@ SILC_SERVER_CMD_FUNC(silcoper) goto out; } + /* Check username */ + username = silc_identifier_check(username, tmp_len, SILC_STRING_UTF8, 128, + &tmp_len); + if (!username) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER, + SILC_STATUS_ERR_BAD_USERNAME, + 0); + goto out; + } + /* Get the admin configuration */ admin = silc_server_config_find_admin(server, cmd->sock->ip, username, client->nickname); @@ -4275,6 +4516,7 @@ SILC_SERVER_CMD_FUNC(silcoper) SILC_STATUS_OK, 0); out: + silc_free(username); silc_server_command_free(cmd); } @@ -4380,8 +4622,14 @@ SILC_SERVER_CMD_FUNC(ban) } /* Now add or delete the information. */ - silc_server_inviteban_process(server, channel->ban_list, - (SilcUInt8)atype[0], args); + if (!silc_server_inviteban_process(server, channel->ban_list, + (SilcUInt8)atype[0], args)) { + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_BAN, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, + 0); + goto out; + } } silc_argument_payload_free(args); } @@ -4395,7 +4643,7 @@ SILC_SERVER_CMD_FUNC(ban) channel->ban_list)), SILC_STR_END); silc_hash_table_list(channel->ban_list, &htl); - while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) + while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2)) list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len, type); silc_hash_table_list_reset(&htl); @@ -4539,7 +4787,7 @@ SILC_SERVER_CMD_FUNC(users) unsigned char lc[4]; SilcUInt32 list_count = 0; SilcUInt16 ident = silc_command_get_ident(cmd->payload); - char *channel_name; + char *channel_name, *channel_namec = NULL; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2); @@ -4555,6 +4803,18 @@ SILC_SERVER_CMD_FUNC(users) goto out; } + /* Check channel name */ + if (channel_name) { + channel_namec = silc_channel_name_check(channel_name, strlen(channel_name), + SILC_STRING_UTF8, 256, NULL); + if (!channel_namec) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS, + SILC_STATUS_ERR_BAD_CHANNEL, 0); + goto out; + } + } + + /* Check Channel ID */ if (channel_id) { id = silc_id_payload_parse_id(channel_id, channel_id_len, NULL); if (!id) { @@ -4572,7 +4832,7 @@ SILC_SERVER_CMD_FUNC(users) channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL); else channel = silc_idlist_find_channel_by_name(server->local_list, - channel_name, NULL); + channel_namec, NULL); if (!channel || (!server->standalone && (channel->disabled || !channel->users_resolved))) { @@ -4605,7 +4865,7 @@ SILC_SERVER_CMD_FUNC(users) channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL); else channel = silc_idlist_find_channel_by_name(server->global_list, - channel_name, NULL); + channel_namec, NULL); if (!channel) { /* Channel really does not exist */ if (id) @@ -4673,6 +4933,7 @@ SILC_SERVER_CMD_FUNC(users) silc_free(id); out: + silc_free(channel_namec); silc_server_command_free(cmd); } @@ -4946,10 +5207,10 @@ SILC_SERVER_CMD_FUNC(close) SILC_GET32_MSB(port, tmp); server_entry = silc_idlist_find_server_by_conn(server->local_list, - name, port, FALSE, NULL); + name, port, TRUE, NULL); if (!server_entry) server_entry = silc_idlist_find_server_by_conn(server->global_list, - name, port, FALSE, NULL); + name, port, TRUE, NULL); if (!server_entry) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE, SILC_STATUS_ERR_NO_SERVER_ID, 0); @@ -4969,6 +5230,11 @@ SILC_SERVER_CMD_FUNC(close) /* Close the connection to the server */ sock = (SilcSocketConnection)server_entry->connection; + if (server_entry->server_type == SILC_BACKUP_ROUTER) { + server->backup_closed = TRUE; + silc_server_backup_del(server, server_entry); + } + server->backup_noswitch = TRUE; if (server->router == server_entry) { server->id_entry->router = NULL;