From ece132680dad90a0967f943d801efdaeda921716 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Tue, 29 Mar 2005 14:14:55 +0000 Subject: [PATCH] Added support for the identifier strings and their validity checking into the server. Added silc_identifier_check for applications to easy check for validity of identifier strings. --- CHANGES | 21 ++++- TODO | 4 +- apps/silcd/command.c | 156 ++++++++++++++++++++++++++++-------- apps/silcd/command_reply.c | 124 +++++++++++++++++++++++++--- apps/silcd/idlist.c | 10 +-- apps/silcd/packet_receive.c | 52 +++++++++--- apps/silcd/server.c | 40 ++++++--- apps/silcd/server_query.c | 24 +++++- apps/silcd/server_util.c | 8 +- apps/silcd/serverid.c | 20 +++-- apps/silcd/serverid.h | 9 ++- lib/silccore/silcidcache.c | 4 +- lib/silccore/silcstatus.h | 4 +- lib/silcutil/silcstrutil.c | 38 ++++++++- lib/silcutil/silcstrutil.h | 46 +++++++++++ lib/silcutil/silcutil.c | 6 +- 16 files changed, 458 insertions(+), 108 deletions(-) diff --git a/CHANGES b/CHANGES index 5ad09448..0b3d0474 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,18 @@ +Tue Mar 29 16:51:35 EEST 2005 Pekka Riikonen + + * Added support for the new identifier strings and their + validity checking. Nicknames, channel names and usernames + can now include practically any kind of letters and various + other characters. Affected files in silcd/ and in libraries. + + NOTE: comparing these strings should now be done with memcmp() + to check binary compatibility. All these strings are normalized + and casing is irrelevant. + + * Added silc_identifier_check to lib/silcutil/silcstrutil.[ch] + as easy function for applications to check whether identifier + strings are valid. + Tue Mar 29 00:45:11 EEST 2005 Pekka Riikonen * Fixed SILC_STRING_LDAP_DN encoding and decoding. Affected @@ -91,8 +106,8 @@ Tue Nov 23 16:54:35 CET 2004 Pekka Riikonen Wed Sep 22 19:46:32 CEST 2004 Patrik Weiskircher * When using silc_net_create_connection[_async], and your system can - create IPv6 sockets, it will try to connect to the IPv6 host. - Now it tries to connect to an IPv4 host if IPv6 fails. Affected + create IPv6 sockets, it will try to connect to the IPv6 host. + Now it tries to connect to an IPv4 host if IPv6 fails. Affected file lib/silcutil/unix/silcunixnet.c Fri Jun 18 19:26:58 CEST 2004 Pekka Riikonen @@ -108,7 +123,7 @@ Sat May 1 13:55:54 CEST 2004 Patrik Weiskircher Fri Apr 30 19:40:28 CEST 2004 Patrik Weiskircher - * Added check to ignore Port value if Initiator is FALSE. + * Added check to ignore Port value if Initiator is FALSE. Remote router coudln't connect if Port was set. Affected file silcd/serverconfig.c diff --git a/TODO b/TODO index 8a86a6fe..18841555 100644 --- a/TODO +++ b/TODO @@ -28,8 +28,6 @@ TODO for SILC Server 1.0 o Basic UTF-8 stringprep profile that makes sure UTF-8 strings are as defined in spec-08 section 3.13. - o Start using the stringprep for identifier strings. - o Check that founder key is distributed ok during backup resuming. o Testing @@ -38,6 +36,8 @@ TODO for SILC Server 1.0 TODO/bugs In SILC Libraries =========================== + o Stringprep checks to the Client Library. + o Add following defines in silcincludes.h and silcclient.h for third-party software: diff --git a/apps/silcd/command.c b/apps/silcd/command.c index d0f4d3e8..34010040 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2004 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 @@ -626,7 +626,7 @@ SILC_SERVER_CMD_FUNC(nick) SilcBuffer packet, nidp, oidp = NULL; SilcClientID *new_id; SilcUInt32 nick_len; - char *nick; + unsigned char *nick = NULL; SilcUInt16 ident = silc_command_get_ident(cmd->payload); int nickfail = 0; @@ -635,31 +635,42 @@ SILC_SERVER_CMD_FUNC(nick) 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 */ + nick = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, + &nick_len); + if (!nick) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_free(nick); goto out; } /* Check for same nickname */ - if (!strcmp(client->nickname, nick)) { + if (!memcmp(client->nickname, nick, nick_len)) { nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); + silc_free(nick); + nick = client->nickname; 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, + cmd->server->md5hash, nick, nick_len, &new_id)) { nickfail++; if (nickfail > 9) { @@ -667,7 +678,17 @@ SILC_SERVER_CMD_FUNC(nick) SILC_STATUS_ERR_BAD_NICKNAME, 0); goto out; } - snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail); + if (nickfail < 2) { + nick = silc_realloc(nick, sizeof(*nick) * (nick_len + 2)); + if (!nick) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + goto out; + } + nick_len += 2; + nick[nick_len - 1] = '\0'; + } + snprintf(&nick[nick_len - 2], 1, "%d", nickfail); } /* Send notify about nickname change to our router. We send the new @@ -691,7 +712,7 @@ SILC_SERVER_CMD_FUNC(nick) client->id = new_id; silc_free(client->nickname); - client->nickname = strdup(nick); + client->nickname = nick; /* Update client cache */ silc_idcache_add(server->local_list->clients, client->nickname, @@ -717,7 +738,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); @@ -1540,7 +1561,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; @@ -1549,6 +1570,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); @@ -1691,6 +1722,7 @@ SILC_SERVER_CMD_FUNC(info) silc_buffer_free(idp); out: + silc_free(dest_server); silc_server_command_free(cmd); } @@ -2326,12 +2358,17 @@ 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; + } - if (silc_server_name_bad_chchars(channel_name, tmp_len) == TRUE) { + /* Check for valid channel name */ + channel_name = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, 256, + &tmp_len); + if (!channel_name) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, SILC_STATUS_ERR_BAD_CHANNEL, 0); goto out; @@ -2551,7 +2588,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); @@ -2566,7 +2603,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); @@ -2682,6 +2729,7 @@ SILC_SERVER_CMD_FUNC(motd) } out: + silc_free(dest_server); silc_server_command_free(cmd); } @@ -3824,7 +3872,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; @@ -3845,6 +3893,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); @@ -3917,6 +3975,7 @@ SILC_SERVER_CMD_FUNC(oper) SILC_STATUS_OK, 0); out: + silc_free(username); silc_server_command_free(cmd); } @@ -4040,8 +4099,7 @@ SILC_SERVER_CMD_FUNC(watch) SilcServer server = cmd->server; char *add_nick, *del_nick; SilcUInt32 add_nick_len, del_nick_len, tmp_len, pk_len; - char nick[128 + 1]; - unsigned char hash[16], *tmp, *pk; + unsigned char hash[16], *tmp, *pk, *nick; SilcClientEntry client; SilcClientID *client_id = NULL; @@ -4126,16 +4184,20 @@ SILC_SERVER_CMD_FUNC(watch) 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; @@ -4143,8 +4205,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, @@ -4153,6 +4214,7 @@ 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; } @@ -4163,11 +4225,14 @@ SILC_SERVER_CMD_FUNC(watch) /* 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; @@ -4175,8 +4240,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(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, @@ -4184,7 +4248,8 @@ SILC_SERVER_CMD_FUNC(watch) /* 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; } @@ -4195,6 +4260,7 @@ 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 */ @@ -4310,7 +4376,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; @@ -4337,6 +4403,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); @@ -4407,6 +4483,7 @@ SILC_SERVER_CMD_FUNC(silcoper) SILC_STATUS_OK, 0); out: + silc_free(username); silc_server_command_free(cmd); } @@ -4671,7 +4748,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 = NULL; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2); @@ -4687,6 +4764,18 @@ SILC_SERVER_CMD_FUNC(users) goto out; } + /* Check channel name */ + if (channel_name) { + channel_name = silc_identifier_check(channel_name, strlen(channel_name), + SILC_STRING_UTF8, 256, NULL); + if (!channel_name) { + 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) { @@ -4805,6 +4894,7 @@ SILC_SERVER_CMD_FUNC(users) silc_free(id); out: + silc_free(channel_name); silc_server_command_free(cmd); } diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 9acb1ed8..99f45955 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2004 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 @@ -197,6 +197,14 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen); + /* Check for valid username */ + username = silc_identifier_check(username, strlen(username), + SILC_STRING_UTF8, 128, NULL); + if (!username) { + SILC_LOG_ERROR(("Malformed username received in WHOIS reply")); + return FALSE; + } + /* Check if we have this client cached already. */ client = silc_idlist_find_client_by_id(server->local_list, client_id, @@ -210,21 +218,35 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type != SILC_SERVER) + if (server->server_type != SILC_SERVER) { + silc_free(username); return FALSE; + } /* Take hostname out of nick string if it includes it. */ silc_parse_userfqdn(nickname, &nick, &servername); + /* Check nickname */ + nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!nickname) { + SILC_LOG_ERROR(("Malformed nickname received in WHOIS reply")); + silc_free(username); + return FALSE; + } + silc_free(nick); + nick = nickname; + /* We don't have that client anywhere, add it. The client is added to global list since server didn't have it in the lists so it must be global. */ - client = silc_idlist_add_client(server->global_list, nick, - strdup(username), + client = silc_idlist_add_client(server->global_list, nick, username, strdup(realname), client_id, cmd->sock->user_data, NULL, 0); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + silc_free(nick); + silc_free(username); return FALSE; } @@ -241,6 +263,17 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) /* Take hostname out of nick string if it includes it. */ silc_parse_userfqdn(nickname, &nick, &servername); + /* Check nickname */ + nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!nickname) { + SILC_LOG_ERROR(("Malformed nickname received in WHOIS reply")); + silc_free(username); + return FALSE; + } + silc_free(nick); + nick = nickname; + /* Remove the old cache entry */ silc_idcache_del_by_context(global ? server->global_list->clients : server->local_list->clients, client); @@ -251,7 +284,7 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) silc_free(client->servername); client->nickname = nick; - client->username = strdup(username); + client->username = username; client->userinfo = strdup(realname); client->servername = servername; client->mode = mode; @@ -364,6 +397,7 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) } } + silc_free(username); return TRUE; } @@ -455,6 +489,14 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) if (!client_id) return FALSE; + /* Check for valid username */ + username = silc_identifier_check(username, strlen(username), + SILC_STRING_UTF8, 128, NULL); + if (!username) { + SILC_LOG_ERROR(("Malformed username received in WHOWAS reply")); + return FALSE; + } + /* Check if we have this client cached already. */ client = silc_idlist_find_client_by_id(server->local_list, client_id, @@ -468,22 +510,37 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type != SILC_SERVER) + if (server->server_type != SILC_SERVER) { + silc_free(username); return FALSE; + } /* Take hostname out of nick string if it includes it. */ silc_parse_userfqdn(nickname, &nick, &servername); + /* Check nickname */ + nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!nickname) { + SILC_LOG_ERROR(("Malformed nickname received in WHOWAS reply")); + silc_free(username); + return FALSE; + } + silc_free(nick); + nick = nickname; + /* We don't have that client anywhere, add it. The client is added to global list since server didn't have it in the lists so it must be global. */ - client = silc_idlist_add_client(server->global_list, nick, - strdup(username), strdup(realname), + client = silc_idlist_add_client(server->global_list, nick, username, + strdup(realname), silc_id_dup(client_id, SILC_ID_CLIENT), cmd->sock->user_data, NULL, SILC_ID_CACHE_EXPIRE_DEF); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + silc_free(nick); + silc_free(username); return FALSE; } @@ -497,12 +554,23 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) /* Take hostname out of nick string if it includes it. */ silc_parse_userfqdn(nickname, &nick, &servername); + /* Check nickname */ + nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!nickname) { + SILC_LOG_ERROR(("Malformed nickname received in WHOWAS reply")); + silc_free(username); + return FALSE; + } + silc_free(nick); + nick = nickname; + silc_free(client->nickname); silc_free(client->username); silc_free(client->servername); client->nickname = nick; - client->username = strdup(username); + client->username = username; client->servername = servername; client->data.status |= SILC_IDLIST_STATUS_RESOLVED; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; @@ -527,6 +595,7 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) } silc_free(client_id); + silc_free(username); return TRUE; } @@ -617,9 +686,20 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) goto error; /* Take nickname */ - if (name) + if (name) { silc_parse_userfqdn(name, &nick, NULL); + /* Check nickname */ + name = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!name) { + SILC_LOG_ERROR(("Malformed nickname received in IDENTIFY reply")); + return FALSE; + } + silc_free(nick); + nick = name; + } + /* We don't have that client anywhere, add it. The client is added to global list since server didn't have it in the lists so it must be global. */ @@ -629,6 +709,7 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) NULL, time(NULL) + 300); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + silc_free(nick); goto error; } client->data.status |= SILC_IDLIST_STATUS_REGISTERED; @@ -643,6 +724,16 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) if (name) { silc_parse_userfqdn(name, &nick, NULL); + /* Check nickname */ + name = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!name) { + SILC_LOG_ERROR(("Malformed nickname received in IDENTIFY reply")); + return FALSE; + } + silc_free(nick); + nick = name; + /* Remove the old cache entry */ silc_idcache_del_by_context(global ? server->global_list->clients : server->local_list->clients, client); @@ -809,7 +900,7 @@ SILC_SERVER_CMD_REPLY_FUNC(info) SilcServerEntry entry; SilcServerID *server_id; SilcUInt32 tmp_len; - unsigned char *tmp, *name; + unsigned char *tmp, *name = NULL; COMMAND_CHECK_STATUS; @@ -823,7 +914,13 @@ SILC_SERVER_CMD_REPLY_FUNC(info) /* Get the name */ name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); - if (tmp_len > 256) + if (!name) + goto out; + + /* Check server name */ + name = silc_identifier_check(name, tmp_len, SILC_STRING_UTF8, + 256, NULL); + if (!name) goto out; entry = silc_idlist_find_server_by_id(server->local_list, server_id, @@ -839,6 +936,7 @@ SILC_SERVER_CMD_REPLY_FUNC(info) cmd->sock); if (!entry) { silc_free(server_id); + silc_free(name); goto out; } entry->data.status |= SILC_IDLIST_STATUS_REGISTERED; @@ -852,6 +950,8 @@ SILC_SERVER_CMD_REPLY_FUNC(info) entry->server_info = tmp ? strdup(tmp) : NULL; + silc_free(name); + out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO); err: diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index 2562c0f0..91ac9cf2 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.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 @@ -419,7 +419,6 @@ int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname, while (silc_idcache_list_next(list, &id_cache)) (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context; - silc_idcache_list_free(list); SILC_LOG_DEBUG(("Found total %d clients", *clients_count)); @@ -439,13 +438,10 @@ int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname, SilcIDCacheEntry id_cache = NULL; unsigned char hash[32]; SilcClientID client_id; - char nick[128 + 1]; SILC_LOG_DEBUG(("Start")); - memset(nick, 0, sizeof(nick)); - silc_to_lower(nickname, nick, sizeof(nick) - 1); - silc_hash_make(md5hash, nick, strlen(nick), hash); + silc_hash_make(md5hash, nickname, strlen(nickname), hash); /* As the Client ID is hashed in the ID cache by hashing only the hash from the Client ID, we can do a lookup with only the hash not the @@ -552,7 +548,7 @@ silc_idlist_replace_client_id(SilcServer server, silc_free(client->id); silc_free(client->nickname); client->id = new_id; - client->nickname = nickname ? strdup(nickname) : NULL; + client->nickname = nickname ? silc_memdup(nickname, strlen(nickname)) : NULL; /* Check if anyone is watching new nickname */ if (server->server_type == SILC_ROUTER) diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 1215e604..01b072a0 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2004 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 @@ -2256,9 +2256,9 @@ SilcClientEntry silc_server_new_client(SilcServer server, SilcIDListData idata; char *username = NULL, *realname = NULL; SilcUInt16 username_len; - SilcUInt32 id_len; + SilcUInt32 id_len, tmp_len; int ret; - char *hostname, *nickname; + char *hostname, *nickname, *tmp; int nickfail = 0; SILC_LOG_DEBUG(("Creating new client")); @@ -2322,16 +2322,32 @@ SilcClientEntry silc_server_new_client(SilcServer server, return NULL; } - if (username_len > 128) + if (username_len > 128) { username[128] = '\0'; + username_len = 128; + } - /* Check for bad characters for nickname, and modify the nickname if - it includes those. */ - if (silc_server_name_bad_chars(username, username_len)) { - nickname = silc_server_name_modify_bad(username, username_len); - } else { - nickname = strdup(username); + /* Check for valid username string */ + tmp = silc_identifier_check(username, username_len, SILC_STRING_UTF8, 128, + &tmp_len); + if (!tmp) { + silc_free(username); + silc_free(realname); + SILC_LOG_ERROR(("Client %s (%s) sent bad username string, closing " + "connection", sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + SILC_STATUS_ERR_INCOMPLETE_INFORMATION, + NULL); + if (sock->user_data) + silc_server_free_sock_user_data(server, sock, NULL); + return NULL; } + silc_free(username); + username = tmp; + username_len = tmp_len; + + /* Nickname is initially same as username */ + nickname = silc_memdup(username, username_len); /* Make sanity checks for the hostname of the client. If the hostname is provided in the `username' check that it is the same than the @@ -2422,7 +2438,8 @@ SilcClientEntry silc_server_new_client(SilcServer server, /* Create Client ID */ while (!silc_id_create_client_id(server, server->id, server->rng, - server->md5hash, nickname, &client_id)) { + server->md5hash, nickname, + strlen(nickname), &client_id)) { nickfail++; if (nickfail > 9) { silc_server_disconnect_remote(server, sock, @@ -3835,12 +3852,13 @@ void silc_server_resume_client(SilcServer server, } /* If the ID is not based in our ID then change it */ - if (!SILC_ID_COMPARE(detached_client->id, server->id, + if (!SILC_ID_COMPARE(detached_client->id, server->id, server->id->ip.data_len)) { silc_free(client_id); while (!silc_id_create_client_id(server, server->id, server->rng, server->md5hash, detached_client->nickname, + strlen(detached_client->nickname), &client_id)) { nickfail++; if (nickfail > 9) { @@ -3851,8 +3869,16 @@ void silc_server_resume_client(SilcServer server, silc_server_free_sock_user_data(server, sock, NULL); return; } + if (nickfail < 2) { + detached_client->nickname = + silc_realloc(detached_client->nickname, + sizeof(*detached_client->nickname) * + (strlen(detached_client->nickname) + 2)); + detached_client-> + nickname[strlen(detached_client->nickname) - 1] = '\0'; + } snprintf(&detached_client-> - nickname[strlen(detached_client->nickname) - 1], 1, + nickname[strlen(detached_client->nickname) - 2], 1, "%d", nickfail); } nick_change = TRUE; diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 05302a7f..7f9dd5d5 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2004 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 @@ -427,11 +427,20 @@ bool silc_server_init(SilcServer server) if (!id) goto err; + /* Check server name */ + server->server_name = + silc_identifier_check(server->config->server_info->server_name, + strlen(server->config->server_info->server_name), + SILC_STRING_LOCALE, 256, NULL); + if (!server->server_name) { + SILC_LOG_ERROR(("Malformed server name string '%s'", + server->config->server_info->server_name)); + goto err; + } + server->id = id; server->id_string = silc_id_id2str(id, SILC_ID_SERVER); server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER); - server->server_name = server->config->server_info->server_name; - server->config->server_info->server_name = NULL; /* Add ourselves to the server list. We don't have a router yet beacuse we haven't established a route yet. It will be done later. @@ -585,8 +594,17 @@ bool silc_server_rehash(SilcServer server) /* Fix the server_name field */ if (strcmp(server->server_name, newconfig->server_info->server_name)) { silc_free(server->server_name); - server->server_name = newconfig->server_info->server_name; - newconfig->server_info->server_name = NULL; + + /* Check server name */ + server->server_name = + silc_identifier_check(newconfig->server_info->server_name, + strlen(newconfig->server_info->server_name), + SILC_STRING_LOCALE, 256, NULL); + if (!server->server_name) { + SILC_LOG_ERROR(("Malformed server name string '%s'", + server->config->server_info->server_name)); + return FALSE; + } /* Update the idcache list with a fresh pointer */ silc_free(server->id_entry->server_name); @@ -970,9 +988,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry) } /* Callback for async connection to remote router */ - + SILC_TASK_CALLBACK(silc_server_connection_established) -{ +{ SilcServer server = app_context; SilcServerConnection sconn = (SilcServerConnection)context; int sock = fd; @@ -1000,14 +1018,14 @@ SILC_TASK_CALLBACK(silc_server_connection_established) } return; } - + SILC_LOG_DEBUG(("Connection to router %s:%d established", sconn->remote_host, sconn->remote_port)); - + /* Continue with key exchange protocol */ silc_server_start_key_exchange(server, sconn, sock); -} - +} + /* Generic routine to use connect to a router. */ SILC_TASK_CALLBACK(silc_server_connect_router) diff --git a/apps/silcd/server_query.c b/apps/silcd/server_query.c index 9e8c7bba..48ecd78d 100644 --- a/apps/silcd/server_query.c +++ b/apps/silcd/server_query.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2002 - 2003 Pekka Riikonen + Copyright (C) 2002 - 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 @@ -399,6 +399,17 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) return; } + /* Check nickname */ + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_server_query_free(query); + } + silc_free(query->nickname); + query->nickname = tmp; + } else { /* Parse the IDs included in the query */ query->ids = silc_calloc(argc, sizeof(*query->ids)); @@ -466,6 +477,17 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) return; } + /* Check nickname */ + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_server_query_free(query); + } + silc_free(query->nickname); + query->nickname = tmp; + /* Get the max count of reply messages allowed */ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (tmp && tmp_len == sizeof(SilcUInt32)) diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index d733f78a..a65a2c19 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2004 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 @@ -1728,10 +1728,8 @@ bool silc_server_check_watcher_list(SilcServer server, /* Make hash from the nick, or take it from Client ID */ if (client->nickname) { - char nick[128 + 1]; - memset(nick, 0, sizeof(nick)); - silc_to_lower(client->nickname, nick, sizeof(nick) - 1); - silc_hash_make(server->md5hash, nick, strlen(nick), hash); + silc_hash_make(server->md5hash, client->nickname, + strlen(client->nickname), hash); } else { memset(hash, 0, sizeof(hash)); memcpy(hash, client->id->hash, sizeof(client->id->hash)); diff --git a/apps/silcd/serverid.c b/apps/silcd/serverid.c index 7d8fdea5..aa1dee54 100644 --- a/apps/silcd/serverid.c +++ b/apps/silcd/serverid.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2002 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 @@ -56,21 +56,19 @@ void silc_id_create_server_id(const char *ip, SilcUInt16 port, SilcRng rng, bool silc_id_create_client_id(SilcServer server, SilcServerID *server_id, SilcRng rng, - SilcHash md5hash, char *nickname, + SilcHash md5hash, + unsigned char *nickname, SilcUInt32 nick_len, SilcClientID **new_id) { unsigned char hash[16]; bool finding = FALSE; - char nick[128 + 1]; SILC_LOG_DEBUG(("Creating new Client ID")); *new_id = silc_calloc(1, sizeof(**new_id)); /* Create hash of the nickanem */ - memset(nick, 0, sizeof(nick)); - silc_to_lower(nickname, nick, sizeof(nick) - 1); - silc_hash_make(md5hash, nick, strlen(nick), hash); + silc_hash_make(md5hash, nickname, nick_len, hash); /* Create the ID */ memcpy((*new_id)->ip.data, server_id->ip.data, server_id->ip.data_len); @@ -80,9 +78,9 @@ bool silc_id_create_client_id(SilcServer server, /* Assure that the ID does not exist already */ while (1) { - if (!silc_idlist_find_client_by_id(server->local_list, + if (!silc_idlist_find_client_by_id(server->local_list, *new_id, FALSE, NULL)) - if (!silc_idlist_find_client_by_id(server->global_list, + if (!silc_idlist_find_client_by_id(server->global_list, *new_id, FALSE, NULL)) break; @@ -126,15 +124,15 @@ bool silc_id_create_channel_id(SilcServer server, /* Assure that the ID does not exist already */ while (1) { - if (!silc_idlist_find_channel_by_id(server->local_list, + if (!silc_idlist_find_channel_by_id(server->local_list, *new_id, NULL)) break; (*new_id)->rnd++; - + if (finding && (*new_id)->rnd == 0) return FALSE; - + if (!finding) { (*new_id)->rnd = 0; finding = TRUE; diff --git a/apps/silcd/serverid.h b/apps/silcd/serverid.h index ce70e7af..4f81aa17 100644 --- a/apps/silcd/serverid.h +++ b/apps/silcd/serverid.h @@ -4,13 +4,13 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2002 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -22,11 +22,12 @@ #define SERVERID_H /* Prototypes */ -void silc_id_create_server_id(const char *ip, SilcUInt16 port, SilcRng rng, +void silc_id_create_server_id(const char *ip, SilcUInt16 port, SilcRng rng, SilcServerID **new_id); bool silc_id_create_client_id(SilcServer server, SilcServerID *server_id, SilcRng rng, - SilcHash md5hash, char *nickname, + SilcHash md5hash, unsigned char *nickname, + SilcUInt32 nick_len, SilcClientID **new_id); bool silc_id_create_channel_id(SilcServer server, SilcServerID *router_id, SilcRng rng, diff --git a/lib/silccore/silcidcache.c b/lib/silccore/silcidcache.c index 9d93db20..16e995f8 100644 --- a/lib/silccore/silcidcache.c +++ b/lib/silccore/silcidcache.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2000 - 2004 Pekka Riikonen + Copyright (C) 2000 - 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 @@ -114,7 +114,7 @@ SilcIDCache silc_idcache_alloc(SilcUInt32 count, SilcIdType id_type, SILC_32_TO_PTR(id_type), silc_idcache_destructor, NULL, TRUE); cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL, - silc_hash_string_compare, NULL, + silc_hash_data_compare, NULL, NULL, NULL, TRUE); cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL, NULL, NULL, NULL, NULL, TRUE); diff --git a/lib/silccore/silcstatus.h b/lib/silccore/silcstatus.h index 3b5c7cd0..3f1637b6 100644 --- a/lib/silccore/silcstatus.h +++ b/lib/silccore/silcstatus.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2002 - 2003 Pekka Riikonen + Copyright (C) 2002 - 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 @@ -102,6 +102,8 @@ typedef SilcUInt8 SilcStatus; #define SILC_STATUS_ERR_TIMEDOUT 54 #define SILC_STATUS_ERR_UNSUPPORTED_PUBLIC_KEY 55 #define SILC_STATUS_ERR_OPERATION_ALLOWED 56 +#define SILC_STATUS_ERR_BAD_SERVER 57 +#define SILC_STATUS_ERR_BAD_USERNAME 58 /***/ #define SILC_STATUS_IS_ERROR(status) (status >= SILC_STATUS_ERR_NO_SUCH_NICK) diff --git a/lib/silcutil/silcstrutil.c b/lib/silcutil/silcstrutil.c index d998ebbe..e3b75fbf 100644 --- a/lib/silcutil/silcstrutil.c +++ b/lib/silcutil/silcstrutil.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2002 - 2004 Pekka Riikonen + Copyright (C) 2002 - 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 @@ -273,3 +273,39 @@ char *silc_strncat(char *dest, SilcUInt32 dest_size, return dest; } + +/* Checks that the 'identifier' string is valid identifier string + and does not contain any unassigned or prohibited character. This + function is used to check for valid nicknames, channel names, + server names, usernames, hostnames, service names, algorithm names, + other security property names, and SILC Public Key name. */ + +unsigned char *silc_identifier_check(const unsigned char *identifier, + SilcUInt32 identifier_len, + SilcStringEncoding identifier_encoding, + SilcUInt32 max_allowed_length, + SilcUInt32 *out_len) +{ + unsigned char *utf8s; + SilcUInt32 utf8s_len; + SilcStringprepStatus status; + + if (!identifier || !identifier_len) + return NULL; + + if (max_allowed_length && identifier_len > max_allowed_length) + return NULL; + + status = silc_stringprep(identifier, identifier_len, + identifier_encoding, SILC_IDENTIFIER_PREP, 0, + &utf8s, &utf8s_len, SILC_STRING_UTF8); + if (status != SILC_STRINGPREP_OK) { + SILC_LOG_DEBUG(("silc_stringprep() status error %d", status)); + return NULL; + } + + if (out_len) + *out_len = utf8s_len; + + return utf8s; +} diff --git a/lib/silcutil/silcstrutil.h b/lib/silcutil/silcstrutil.h index fe1243f9..98d0e740 100644 --- a/lib/silcutil/silcstrutil.h +++ b/lib/silcutil/silcstrutil.h @@ -167,4 +167,50 @@ silc_mime_parse(const unsigned char *mime, SilcUInt32 mime_len, char *silc_strncat(char *dest, SilcUInt32 dest_size, const char *src, SilcUInt32 src_len); +/****f* silcutil/SilcStrUtilAPI/silc_identifier_check + * + * SYNOPSIS + * + * unsigned char * + * silc_identifier_check(const unsigned char *identifier, + * SilcUInt32 identifier_len, + * SilcStringEncoding identifier_encoding, + * SilcUInt32 max_allowed_length, + * SilcUInt32 *out_len); + * + * DESCRIPTION + * + * Checks that the 'identifier' string is valid identifier string + * and does not contain any unassigned or prohibited character. This + * function is used to check for valid nicknames, channel names, + * server names, usernames, hostnames, service names, algorithm names, + * other security property names, and SILC Public Key name. + * + * If the 'max_allowed_length' is non-zero the identifier cannot be + * longer than that, and NULL is returned if it is. If zero (0), no + * length limit exist. For nicknames the max length must be 128 bytes + * and for channel names 256 bytes. Other identifiers has no default + * limit, but application may choose one anyway. + * + * Returns the validated string, that the caller must free. Returns + * NULL if the identifier string is not valid or contain unassigned or + * prohibited characters. Such identifier strings must not be used + * SILC protocol. The returned string is always in UTF-8 encoding. + * The length of the returned string is in 'out_len'. + * + * NOTES + * + * In addition of validating the identifier string, this function + * may map characters to other characters or remove characters from the + * original string. This is done as defined in the SILC protocol. Error + * is returned only if the string contains unassigned or prohibited + * characters. The original 'identifier' is not modified at any point. + * + ***/ +unsigned char *silc_identifier_check(const unsigned char *identifier, + SilcUInt32 identifier_len, + SilcStringEncoding identifier_encoding, + SilcUInt32 max_allowed_length, + SilcUInt32 *out_len); + #endif /* SILCSTRUTIL_H */ diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c index 4715e926..c30024e8 100644 --- a/lib/silcutil/silcutil.c +++ b/lib/silcutil/silcutil.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2004 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 @@ -156,7 +156,7 @@ bool silc_parse_userfqdn(const char *string, char **left, char **right) } } else { if (left) - *left = strdup(string); + *left = silc_memdup(string, strlen(string)); } return TRUE; @@ -1046,6 +1046,8 @@ static const SilcStatusMessage silc_status_messages[] = { { STAT(TIMEDOUT), "Service timed out" }, { STAT(UNSUPPORTED_PUBLIC_KEY), "Unsupported public key type" }, { STAT(OPERATION_ALLOWED), "Operation is not allowed" }, + { STAT(BAD_SERVER), "Bad server name" }, + { STAT(BAD_USERNAME), "Bad user name" }, { 0, NULL } }; -- 2.43.0