X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fserver_util.c;h=2cc4907804fabca8ebffc23fe4fb2b43de50d6aa;hp=839599d2cfde0b1f166e504c4c4c5d3014319a6e;hb=413da0f8686910f5e627393157566ae729ca99c4;hpb=050bd9d9e5d843220f3f393a18ab5011622237b9 diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index 839599d2..2cc49078 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -1,10 +1,10 @@ /* - server_util.c + server_util.c Author: Pekka Riikonen - Copyright (C) 1997 - 2002 Pekka Riikonen + Copyright (C) 1997 - 2003 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 @@ -42,7 +42,7 @@ silc_server_remove_clients_channels(SilcServer server, if (!client) return; - SILC_LOG_DEBUG(("Remove client %s from all channels", + SILC_LOG_DEBUG(("Remove client %s from all channels", client->nickname ? client->nickname : (unsigned char *)"")); @@ -72,7 +72,7 @@ silc_server_remove_clients_channels(SilcServer server, /* If there is no global users on the channel anymore mark the channel as local channel. Do not check if the removed client is local client. */ - if (server->server_type != SILC_ROUTER && channel->global_users && + if (server->server_type != SILC_ROUTER && channel->global_users && chl->client->router && !silc_server_channel_has_global(channel)) channel->global_users = FALSE; @@ -113,7 +113,7 @@ silc_server_remove_clients_channels(SilcServer server, } silc_hash_table_list_reset(&htl2); - /* Add the channel to the the channels list to regenerate the + /* Add the channel to the the channels list to regenerate the channel key */ if (!silc_hash_table_find(channels, channel, NULL, NULL)) silc_hash_table_add(channels, channel, channel); @@ -123,7 +123,7 @@ silc_server_remove_clients_channels(SilcServer server, /* This function removes all client entries that are originated from `router' and are owned by `entry'. `router' and `entry' can be same - too. If `server_signoff' is TRUE then SERVER_SIGNOFF notify is + too. If `server_signoff' is TRUE then SERVER_SIGNOFF notify is distributed to our local clients. */ bool silc_server_remove_clients_by_server(SilcServer server, @@ -230,7 +230,7 @@ bool silc_server_remove_clients_by_server(SilcServer server, } silc_idcache_list_free(list); } - + if (silc_idcache_get_all(server->global_list->clients, &list)) { if (silc_idcache_list_first(list, &id_cache)) { @@ -328,7 +328,7 @@ bool silc_server_remove_clients_by_server(SilcServer server, is to be removed for those servers that would like to use that list. */ args = silc_argument_payload_encode(argc, argv, argv_lens, argv_types); - not = silc_notify_payload_encode_args(SILC_NOTIFY_TYPE_SERVER_SIGNOFF, + not = silc_notify_payload_encode_args(SILC_NOTIFY_TYPE_SERVER_SIGNOFF, argc, args); silc_server_packet_send_clients(server, clients, SILC_PACKET_NOTIFY, 0, FALSE, @@ -363,8 +363,8 @@ bool silc_server_remove_clients_by_server(SilcServer server, if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY || !channel->channel_key) continue; - silc_server_send_channel_key(server, NULL, channel, - server->server_type == SILC_ROUTER ? + silc_server_send_channel_key(server, NULL, channel, + server->server_type == SILC_ROUTER ? FALSE : !server->standalone); } silc_hash_table_list_reset(&htl); @@ -386,6 +386,8 @@ silc_server_update_clients_by_real_server(SilcServer server, SilcIDCacheList list; bool tolocal = (to == server->id_entry); + SILC_LOG_DEBUG(("Start")); + if (!silc_idcache_get_all(server->local_list->servers, &list)) return NULL; @@ -394,7 +396,7 @@ silc_server_update_clients_by_real_server(SilcServer server, server_entry = (SilcServerEntry)id_cache->context; if (server_entry != from && (tolocal || server_entry != server->id_entry) && - SILC_ID_COMPARE(server_entry->id, client->id, + SILC_ID_COMPARE(server_entry->id, client->id, client->id->ip.data_len)) { SILC_LOG_DEBUG(("Found (local) %s", silc_id_render(server_entry->id, SILC_ID_SERVER))); @@ -412,6 +414,7 @@ silc_server_update_clients_by_real_server(SilcServer server, } server_entry = server_entry->router; } else { + SILC_LOG_DEBUG(("Server locally connected")); /* If the client is not marked as local then move it to local list since the server is local. */ if (server_entry->server_type != SILC_BACKUP_ROUTER && !local) { @@ -420,6 +423,16 @@ silc_server_update_clients_by_real_server(SilcServer server, client_cache->id, client_cache->context, client_cache->expire, NULL); silc_idcache_del_by_context(server->global_list->clients, client); + + } else if (server->server_type == SILC_BACKUP_ROUTER && local) { + /* If we are backup router and this client is on local list, we + must move it to global list, as it is not currently local to + us (we are not primary). */ + SILC_LOG_DEBUG(("Moving client to global list")); + silc_idcache_add(server->global_list->clients, client_cache->name, + client_cache->id, client_cache->context, + client_cache->expire, NULL); + silc_idcache_del_by_context(server->local_list->clients, client); } } @@ -442,7 +455,7 @@ silc_server_update_clients_by_real_server(SilcServer server, server_entry = (SilcServerEntry)id_cache->context; if (server_entry != from && server_entry != server->id_entry && (tolocal || server_entry != server->id_entry) && - SILC_ID_COMPARE(server_entry->id, client->id, + SILC_ID_COMPARE(server_entry->id, client->id, client->id->ip.data_len)) { SILC_LOG_DEBUG(("Found (global) %s", silc_id_render(server_entry->id, SILC_ID_SERVER))); @@ -459,6 +472,7 @@ silc_server_update_clients_by_real_server(SilcServer server, } server_entry = server_entry->router; } else { + SILC_LOG_DEBUG(("Server locally connected")); /* If the client is marked as local then move it to global list since the server is global. */ if (server_entry->server_type != SILC_BACKUP_ROUTER && local) { @@ -490,7 +504,7 @@ silc_server_update_clients_by_real_server(SilcServer server, to, when we've acting as backup router. If it is FALSE the `to' will be the new source. */ -void silc_server_update_clients_by_server(SilcServer server, +void silc_server_update_clients_by_server(SilcServer server, SilcServerEntry from, SilcServerEntry to, bool resolve_real_server) @@ -500,6 +514,16 @@ void silc_server_update_clients_by_server(SilcServer server, SilcClientEntry client = NULL; bool local; + if (from && from->id) { + SILC_LOG_DEBUG(("Changing from server %s", + silc_id_render(from->id, SILC_ID_SERVER))); + } + if (to && to->id) { + SILC_LOG_DEBUG(("Changing to server %s", + silc_id_render(to->id, SILC_ID_SERVER))); + } + + SILC_LOG_DEBUG(("global list")); local = FALSE; if (silc_idcache_get_all(server->global_list->clients, &list)) { if (silc_idcache_list_first(list, &id_cache)) { @@ -516,16 +540,16 @@ void silc_server_update_clients_by_server(SilcServer server, continue; } - SILC_LOG_DEBUG(("Client %s", + SILC_LOG_DEBUG(("Client %s", silc_id_render(client->id, SILC_ID_CLIENT))); if (client->router) - SILC_LOG_DEBUG(("Client->router %s", + SILC_LOG_DEBUG(("Client->router %s", silc_id_render(client->router->id, SILC_ID_SERVER))); if (from) { if (client->router == from) { if (resolve_real_server) { - client->router = + client->router = silc_server_update_clients_by_real_server(server, from, to, client, local, id_cache); @@ -541,11 +565,18 @@ void silc_server_update_clients_by_server(SilcServer server, } } else { /* All are changed */ + if (resolve_real_server) + /* Call this so that the entry is moved to correct list if + needed. No resolving by real server is actually done. */ + silc_server_update_clients_by_real_server(server, NULL, to, + client, local, + id_cache); + client->router = to; } if (client->router) - SILC_LOG_DEBUG(("Client changed to %s", + SILC_LOG_DEBUG(("Client changed to %s", silc_id_render(client->router->id, SILC_ID_SERVER))); if (!silc_idcache_list_next(list, &id_cache)) @@ -555,6 +586,7 @@ void silc_server_update_clients_by_server(SilcServer server, silc_idcache_list_free(list); } + SILC_LOG_DEBUG(("local list")); local = TRUE; if (silc_idcache_get_all(server->local_list->clients, &list)) { if (silc_idcache_list_first(list, &id_cache)) { @@ -571,16 +603,16 @@ void silc_server_update_clients_by_server(SilcServer server, continue; } - SILC_LOG_DEBUG(("Client %s", + SILC_LOG_DEBUG(("Client %s", silc_id_render(client->id, SILC_ID_CLIENT))); if (client->router) - SILC_LOG_DEBUG(("Client->router %s", + SILC_LOG_DEBUG(("Client->router %s", silc_id_render(client->router->id, SILC_ID_SERVER))); if (from) { if (client->router == from) { if (resolve_real_server) { - client->router = + client->router = silc_server_update_clients_by_real_server(server, from, to, client, local, id_cache); @@ -592,11 +624,18 @@ void silc_server_update_clients_by_server(SilcServer server, } } else { /* All are changed */ + if (resolve_real_server) + /* Call this so that the entry is moved to correct list if + needed. No resolving by real server is actually done. */ + silc_server_update_clients_by_real_server(server, NULL, to, + client, local, + id_cache); + client->router = to; } if (client->router) - SILC_LOG_DEBUG(("Client changed to %s", + SILC_LOG_DEBUG(("Client changed to %s", silc_id_render(client->router->id, SILC_ID_SERVER))); if (!silc_idcache_list_next(list, &id_cache)) @@ -610,7 +649,7 @@ void silc_server_update_clients_by_server(SilcServer server, /* Updates servers that are from `from' to be originated from `to'. This will also update the server's connection to `to's connection. */ -void silc_server_update_servers_by_server(SilcServer server, +void silc_server_update_servers_by_server(SilcServer server, SilcServerEntry from, SilcServerEntry to) { @@ -650,9 +689,18 @@ void silc_server_update_servers_by_server(SilcServer server, whether this server is in our cell, but not connected to us (in which case we must remove it). */ - if (server_entry->router == from) { + if (from) { + if (server_entry->router == from) { + SILC_LOG_DEBUG(("Updating server (local) %s", + server_entry->server_name ? + server_entry->server_name : "")); + server_entry->router = to; + server_entry->connection = to->connection; + } + } else { + /* Update all */ SILC_LOG_DEBUG(("Updating server (local) %s", - server_entry->server_name ? + server_entry->server_name ? server_entry->server_name : "")); server_entry->router = to; server_entry->connection = to->connection; @@ -696,9 +744,18 @@ void silc_server_update_servers_by_server(SilcServer server, whether this server is in our cell, but not connected to us (in which case we must remove it). */ - if (server_entry->router == from) { + if (from) { + if (server_entry->router == from) { + SILC_LOG_DEBUG(("Updating server (global) %s", + server_entry->server_name ? + server_entry->server_name : "")); + server_entry->router = to; + server_entry->connection = to->connection; + } + } else { + /* Update all */ SILC_LOG_DEBUG(("Updating server (global) %s", - server_entry->server_name ? + server_entry->server_name ? server_entry->server_name : "")); server_entry->router = to; server_entry->connection = to->connection; @@ -846,7 +903,7 @@ void silc_server_remove_servers_by_server(SilcServer server, /* Removes channels that are from `from. */ -void silc_server_remove_channels_by_server(SilcServer server, +void silc_server_remove_channels_by_server(SilcServer server, SilcServerEntry from) { SilcIDCacheList list = NULL; @@ -871,7 +928,7 @@ void silc_server_remove_channels_by_server(SilcServer server, /* Updates channels that are from `from' to be originated from `to'. */ -void silc_server_update_channels_by_server(SilcServer server, +void silc_server_update_channels_by_server(SilcServer server, SilcServerEntry from, SilcServerEntry to) { @@ -885,8 +942,13 @@ void silc_server_update_channels_by_server(SilcServer server, if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { channel = (SilcChannelEntry)id_cache->context; - if (channel->router == from) + if (from) { + if (channel->router == from) + channel->router = to; + } else { + /* Update all */ channel->router = to; + } if (!silc_idcache_list_next(list, &id_cache)) break; } @@ -995,9 +1057,9 @@ bool silc_server_channel_delete(SilcServer server, return TRUE; } -/* Returns TRUE if the given client is on the channel. FALSE if not. +/* 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 + always in up to date thus we can only check the channel list from `client' which is faster than checking the user list from `channel'. */ bool silc_server_client_on_channel(SilcClientEntry client, @@ -1007,7 +1069,7 @@ bool silc_server_client_on_channel(SilcClientEntry client, if (!client || !channel) return FALSE; - return silc_hash_table_find(client->channels, channel, NULL, + return silc_hash_table_find(client->channels, channel, NULL, (void **)chl); } @@ -1017,6 +1079,27 @@ bool silc_server_name_bad_chars(const char *name, SilcUInt32 name_len) { int i; + for (i = 0; i < name_len; i++) { + if (!isascii(name[i])) + return TRUE; + if (name[i] <= 32) return TRUE; + if (name[i] == ' ') return TRUE; + if (name[i] == '*') return TRUE; + if (name[i] == '?') return TRUE; + if (name[i] == ',') return TRUE; + if (name[i] == '@') return TRUE; + if (name[i] == '!') return TRUE; + } + + return FALSE; +} + +/* Same as silc_server_name_bad_chars but check for channel names. */ + +bool silc_server_name_bad_chchars(const char *name, SilcUInt32 name_len) +{ + int i; + for (i = 0; i < name_len; i++) { if (!isascii(name[i])) return TRUE; @@ -1072,7 +1155,7 @@ SilcUInt32 silc_server_num_sockets_by_ip(SilcServer server, const char *ip, by `ip' or `hostname', `port', and `type'. Returns 0 if socket connections does not exist. If `ip' is provided then `hostname' is ignored. */ -SilcUInt32 silc_server_num_sockets_by_remote(SilcServer server, +SilcUInt32 silc_server_num_sockets_by_remote(SilcServer server, const char *ip, const char *hostname, SilcUInt16 port, @@ -1095,12 +1178,12 @@ SilcUInt32 silc_server_num_sockets_by_remote(SilcServer server, return count; } -/* Finds locally cached public key by the public key received in the SKE. +/* Finds locally cached public key by the public key received in the SKE. If we have it locally cached then we trust it and will use it in the authentication protocol. Returns the locally cached public key or NULL if we do not find the public key. */ -SilcPublicKey silc_server_find_public_key(SilcServer server, +SilcPublicKey silc_server_find_public_key(SilcServer server, SilcHashTable local_public_keys, SilcPublicKey remote_public_key) { @@ -1110,7 +1193,7 @@ SilcPublicKey silc_server_find_public_key(SilcServer server, silc_hash_table_count(local_public_keys))); if (!silc_hash_table_find_ext(local_public_keys, remote_public_key, - (void **)&cached_key, NULL, + (void **)&cached_key, NULL, silc_hash_public_key, NULL, silc_hash_public_key_compare, NULL)) { SILC_LOG_ERROR(("Public key not found")); @@ -1138,8 +1221,10 @@ SilcPublicKey silc_server_get_public_key(SilcServer server, assert(silc_hash_table_count(local_public_keys) < 2); silc_hash_table_list(local_public_keys, &htl); - if (!silc_hash_table_get(&htl, NULL, (void **)&cached_key)) + if (!silc_hash_table_get(&htl, NULL, (void **)&cached_key)) { + silc_hash_table_list_reset(&htl); return NULL; + } silc_hash_table_list_reset(&htl); return cached_key; @@ -1149,7 +1234,7 @@ SilcPublicKey silc_server_get_public_key(SilcServer server, checks for example whether there is too much connections for this host, and required version for the host etc. */ -bool silc_server_connection_allowed(SilcServer server, +bool silc_server_connection_allowed(SilcServer server, SilcSocketConnection sock, SilcSocketType type, SilcServerConfigConnParams *global, @@ -1170,18 +1255,18 @@ bool silc_server_connection_allowed(SilcServer server, /* Check version */ - l_protocol_version = - silc_version_to_num(params && params->version_protocol ? - params->version_protocol : + l_protocol_version = + silc_version_to_num(params && params->version_protocol ? + params->version_protocol : global->version_protocol); - l_software_version = - silc_version_to_num(params && params->version_software ? - params->version_software : + l_software_version = + silc_version_to_num(params && params->version_software ? + params->version_software : global->version_software); - l_vendor_version = (params && params->version_software_vendor ? - params->version_software_vendor : + l_vendor_version = (params && params->version_software_vendor ? + params->version_software_vendor : global->version_software_vendor); - + if (ske && silc_ske_parse_version(ske, &r_protocol_version, NULL, &r_software_version, NULL, &r_vendor_version)) { @@ -1192,7 +1277,8 @@ bool silc_server_connection_allowed(SilcServer server, r_protocol_version < l_protocol_version) { SILC_LOG_INFO(("Connection %s (%s) is too old version", sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_BAD_VERSION, "You support too old protocol version"); return FALSE; @@ -1203,18 +1289,20 @@ bool silc_server_connection_allowed(SilcServer server, r_software_version < l_software_version) { SILC_LOG_INFO(("Connection %s (%s) is too old version", sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_BAD_VERSION, "You support too old software version"); return FALSE; } /* Regex match vendor version */ - if (l_vendor_version && r_vendor_version && + if (l_vendor_version && r_vendor_version && !silc_string_match(l_vendor_version, r_vendor_version)) { SILC_LOG_INFO(("Connection %s (%s) is unsupported version", sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_BAD_VERSION, "Your software is not supported"); return FALSE; @@ -1232,7 +1320,8 @@ bool silc_server_connection_allowed(SilcServer server, if (max_hosts && conn_number >= max_hosts) { SILC_LOG_INFO(("Server is full, closing %s (%s) connection", sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_RESOURCE_LIMIT, "Server is full, try again later"); return FALSE; @@ -1241,7 +1330,8 @@ bool silc_server_connection_allowed(SilcServer server, if (num_sockets >= max_per_host) { SILC_LOG_INFO(("Too many connections from %s (%s), closing connection", sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_RESOURCE_LIMIT, "Too many connections from your host"); return FALSE; @@ -1286,7 +1376,7 @@ bool silc_server_check_cmode_rights(SilcServer server, return FALSE; } } - + if (mode & SILC_CHANNEL_MODE_PASSPHRASE) { if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) { if (is_op && !is_fo) @@ -1310,7 +1400,7 @@ bool silc_server_check_cmode_rights(SilcServer server, return FALSE; } } - + if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) { if (is_op && !is_fo) @@ -1322,7 +1412,7 @@ bool silc_server_check_cmode_rights(SilcServer server, return FALSE; } } - + if (mode & SILC_CHANNEL_MODE_SILENCE_USERS) { if (!(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS)) { if (is_op && !is_fo) @@ -1334,7 +1424,7 @@ bool silc_server_check_cmode_rights(SilcServer server, return FALSE; } } - + if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS) { if (!(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS)) { if (is_op && !is_fo) @@ -1346,7 +1436,19 @@ bool silc_server_check_cmode_rights(SilcServer server, return FALSE; } } - + + if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) { + if (!(channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)) { + if (is_op && !is_fo) + return FALSE; + } + } else { + if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) { + if (is_op && !is_fo) + return FALSE; + } + } + return TRUE; } @@ -1410,14 +1512,14 @@ void silc_server_send_connect_notifys(SilcServer server, SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("There are %d clients, %d servers and %d " "routers in SILC Network", - server->stat.clients, server->stat.servers + 1, + server->stat.clients, server->stat.servers, server->stat.routers)); } else { if (server->stat.clients && server->stat.servers + 1) SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("There are %d clients, %d servers and %d " "routers in SILC Network", - server->stat.clients, server->stat.servers + 1, + server->stat.clients, server->stat.servers, (server->standalone ? 0 : !server->stat.routers ? 1 : server->stat.routers))); @@ -1427,12 +1529,12 @@ void silc_server_send_connect_notifys(SilcServer server, SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("There are %d clients on %d server in our cell", server->stat.cell_clients, - server->stat.cell_servers + 1)); + server->stat.cell_servers)); if (server->server_type == SILC_ROUTER) { SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("I have %d clients, %d channels, %d servers and " "%d routers", - server->stat.my_clients, + server->stat.my_clients, server->stat.my_channels, server->stat.my_servers, server->stat.my_routers)); @@ -1480,7 +1582,7 @@ void silc_server_kill_client(SilcServer server, { SilcBuffer killed, killer; - SILC_LOG_DEBUG(("Killing client %s", + SILC_LOG_DEBUG(("Killing client %s", silc_id_render(remote_client->id, SILC_ID_CLIENT))); /* Send the KILL notify packets. First send it to the channel, then @@ -1493,7 +1595,7 @@ void silc_server_kill_client(SilcServer server, /* Send KILLED notify to the channels. It is not sent to the client as it will be sent differently destined directly to the client and not to the channel. */ - silc_server_send_notify_on_channels(server, remote_client, + silc_server_send_notify_on_channels(server, remote_client, remote_client, SILC_NOTIFY_TYPE_KILLED, 3, killed->data, killed->len, comment, comment ? strlen(comment) : 0, @@ -1506,15 +1608,15 @@ void silc_server_kill_client(SilcServer server, /* Send KILLED notify to the client directly */ if (remote_client->connection || remote_client->router) - silc_server_send_notify_killed(server, remote_client->connection ? - remote_client->connection : + silc_server_send_notify_killed(server, remote_client->connection ? + remote_client->connection : remote_client->router->connection, FALSE, - remote_client->id, comment, + remote_client->id, comment, killer_id, killer_id_type); /* Remove the client from all channels. This generates new keys to the channels as well. */ - silc_server_remove_from_channels(server, NULL, remote_client, FALSE, + silc_server_remove_from_channels(server, NULL, remote_client, FALSE, NULL, TRUE, TRUE); /* Remove the client entry, If it is locally connected then we will also @@ -1543,7 +1645,7 @@ void silc_server_kill_client(SilcServer server, if (!silc_idlist_del_client(server->global_list, remote_client)) { /* Remove this client from watcher list if it is */ silc_server_del_from_watcher_list(server, remote_client); - silc_idlist_del_client(server->local_list, remote_client); + silc_idlist_del_client(server->local_list, remote_client); } } @@ -1558,8 +1660,8 @@ typedef struct { const char *new_nick; } WatcherNotifyContext; -static void -silc_server_check_watcher_list_foreach(void *key, void *context, +static void +silc_server_check_watcher_list_foreach(void *key, void *context, void *user_context) { WatcherNotifyContext *notify = user_context; @@ -1579,10 +1681,10 @@ silc_server_check_watcher_list_foreach(void *key, void *context, silc_id_render(entry->id, SILC_ID_CLIENT))); /* Send the WATCH notify */ - silc_server_send_notify_watch(notify->server, sock, entry, - notify->client, + silc_server_send_notify_watch(notify->server, sock, entry, + notify->client, notify->new_nick ? notify->new_nick : - (const char *)notify->client->nickname, + (const char *)notify->client->nickname, notify->notify); } } @@ -1684,7 +1786,7 @@ bool silc_server_force_cumode_change(SilcServer server, 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_server_send_notify_to_channel(server, sock, channel, FALSE, TRUE, SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3, idp1->data, idp1->len, cumode, sizeof(cumode), @@ -1725,7 +1827,7 @@ bool silc_server_inviteban_match(SilcServer server, SilcHashTable list, unsigned char *tmp = NULL; SilcUInt32 len = 0, t; SilcHashTableList htl; - SilcBuffer entry, idp = NULL; + SilcBuffer entry, idp = NULL, pkp = NULL; bool ret = FALSE; if (type < 1 || type > 3 || !check) @@ -1737,9 +1839,11 @@ bool silc_server_inviteban_match(SilcServer server, SilcHashTable list, return FALSE; } if (type == 2) { - tmp = silc_pkcs_public_key_encode(check, &len); - if (!tmp) + pkp = silc_pkcs_public_key_payload_encode(check); + if (!pkp) return FALSE; + tmp = pkp->data; + len = pkp->len; } if (type == 3) { idp = silc_id_payload_encode(check, SILC_ID_CLIENT); @@ -1766,9 +1870,10 @@ bool silc_server_inviteban_match(SilcServer server, SilcHashTable list, } silc_hash_table_list_reset(&htl); - if (!idp) + if (type == 1) silc_free(tmp); silc_buffer_free(idp); + silc_buffer_free(pkp); return ret; } @@ -1783,10 +1888,10 @@ void silc_server_inviteban_process(SilcServer server, SilcHashTable list, SilcHashTableList htl; SILC_LOG_DEBUG(("Processing invite/ban for %s action", - action == 0x00 ? "ADD" : "DEL")); + action == 0x01 ? "DEL" : "ADD")); /* Add the information to invite list */ - if (action == 0x00) { + if (action == 0x00 || action == 0x03) { /* Traverse all arguments and add to the hash table according to their type. */ tmp = silc_argument_get_first_arg(args, &type, &len); @@ -1794,15 +1899,61 @@ void silc_server_inviteban_process(SilcServer server, SilcHashTable list, if (type == 1) { /* Invite string. Get the old invite string from hash table and append this at the end of the existing one. */ - if (!silc_hash_table_find(list, (void *)1, NULL, (void **)&tmp2)) { + if (!silc_hash_table_find(list, (void *)1, NULL, (void *)&tmp2)) { tmp2 = silc_calloc(1, sizeof(*tmp2)); silc_hash_table_add(list, (void *)1, tmp2); } - if (tmp[len - 1] == ',') - tmp[len - 1] = '\0'; - if (len) { - silc_buffer_strformat(tmp2, tmp, SILC_STR_END); - silc_buffer_strformat(tmp2, ",", SILC_STR_END); + + /* Check that the string is not part of invite string already */ + if (action == 0x00) { + if (silc_string_match(tmp2->data, tmp)) + break; + + if (len) { + if (tmp[len - 1] == ',') + tmp[len - 1] = '\0'; + silc_buffer_strformat(tmp2, tmp, SILC_STR_END); + silc_buffer_strformat(tmp2, ",", SILC_STR_END); + } + } else { + /* Announced list. Check each entry in the list */ + unsigned char e[256]; + char *start, *end, *n, *rtmp; + int i, k; + + rtmp = silc_memdup(tmp, len); + for (i = 0, k = 0; i < len; i++) { + if (tmp[i] != ',') + continue; + + memset(e, 0, sizeof(e)); + silc_strncat(e, sizeof(e), tmp + k, i - k); + if (!silc_string_match(tmp2->data, e)) { + k = i + 1; + continue; + } + + /* Matches. Delete it since we have it already */ + start = strstr(rtmp, e); + if (start && strlen(start) >= (i - k)) { + end = start + (i - k); + n = silc_calloc(strlen(rtmp) - (i - k), sizeof(*n)); + strncat(n, rtmp, start - rtmp); + if (strlen(end) > 1) + strncat(n, end + 1, ((rtmp + strlen(rtmp)) - end) - 1); + silc_free(rtmp); + rtmp = n; + } + + k = i + 1; + } + + /* Save the part that we didn't already have. */ + if (strlen(rtmp) > 1) { + silc_buffer_strformat(tmp2, rtmp, SILC_STR_END); + silc_buffer_strformat(tmp2, ",", SILC_STR_END); + } + silc_free(rtmp); } } else if (type == 2) { @@ -1873,7 +2024,8 @@ void silc_server_inviteban_process(SilcServer server, SilcHashTable list, end = start + len; n = silc_calloc(strlen(string) - len, sizeof(*n)); strncat(n, string, start - string); - strncat(n, end + 1, ((string + strlen(string)) - end) - 1); + if (strlen(end) > 1) + strncat(n, end + 1, ((string + strlen(string)) - end) - 1); silc_free(tmp2->head); silc_buffer_set(tmp2, n, strlen(n)); } @@ -1930,3 +2082,244 @@ void silc_server_create_connections(SilcServer server) silc_server_connect_to_router, server, 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); } + +static void +silc_server_process_channel_pk_destruct(void *key, void *context, + void *user_context) +{ + silc_free(key); + silc_pkcs_public_key_free(context); +} + +/* Processes a channel public key, either adds or removes it. */ + +SilcStatus +silc_server_process_channel_pk(SilcServer server, + SilcChannelEntry channel, + SilcUInt32 type, const unsigned char *pk, + SilcUInt32 pk_len) +{ + unsigned char pkhash[20]; + SilcPublicKey chpk; + + SILC_LOG_DEBUG(("Processing channel public key")); + + if (!pk || !pk_len) + return SILC_STATUS_ERR_NOT_ENOUGH_PARAMS; + + /* Decode the public key */ + if (!silc_pkcs_public_key_payload_decode((unsigned char *)pk, pk_len, &chpk)) + return SILC_STATUS_ERR_UNSUPPORTED_PUBLIC_KEY; + + /* Create channel public key list (hash table) if needed */ + if (!channel->channel_pubkeys) { + channel->channel_pubkeys = + silc_hash_table_alloc(0, silc_hash_data, (void *)20, + silc_hash_data_compare, (void *)20, + silc_server_process_channel_pk_destruct, channel, + TRUE); + } + + /* Create SHA-1 digest of the public key data */ + silc_hash_make(server->sha1hash, pk + 4, pk_len - 4, pkhash); + + if (type == 0x00) { + /* Add new public key to channel public key list */ + SILC_LOG_DEBUG(("Add new channel public key to channel %s", + channel->channel_name)); + + /* Check for resource limit */ + if (silc_hash_table_count(channel->channel_pubkeys) > 64) { + silc_pkcs_public_key_free(chpk); + return SILC_STATUS_ERR_RESOURCE_LIMIT; + } + + /* Add if doesn't exist already */ + if (!silc_hash_table_find(channel->channel_pubkeys, pkhash, + NULL, NULL)) + silc_hash_table_add(channel->channel_pubkeys, silc_memdup(pkhash, 20), + chpk); + } else if (type == 0x01) { + /* Delete public key from channel public key list */ + SILC_LOG_DEBUG(("Delete a channel public key from channel %s", + channel->channel_name)); + if (!silc_hash_table_del(channel->channel_pubkeys, pkhash)) + silc_pkcs_public_key_free(chpk); + } else { + silc_pkcs_public_key_free(chpk); + return SILC_STATUS_ERR_NOT_ENOUGH_PARAMS; + } + + return SILC_STATUS_OK; +} + +/* Returns the channel public keys as Argument List payload. */ + +SilcBuffer silc_server_get_channel_pk_list(SilcServer server, + SilcChannelEntry channel, + bool announce, + bool delete) +{ + SilcHashTableList htl; + SilcBuffer list, pkp; + SilcPublicKey pk; + + SILC_LOG_DEBUG(("Encoding channel public keys list")); + + if (!channel->channel_pubkeys || + !silc_hash_table_count(channel->channel_pubkeys)) + return NULL; + + /* Encode the list */ + list = silc_buffer_alloc_size(2); + silc_buffer_format(list, + SILC_STR_UI_SHORT(silc_hash_table_count( + channel->channel_pubkeys)), + SILC_STR_END); + + silc_hash_table_list(channel->channel_pubkeys, &htl); + while (silc_hash_table_get(&htl, NULL, (void **)&pk)) { + pkp = silc_pkcs_public_key_payload_encode(pk); + list = silc_argument_payload_encode_one(list, pkp->data, pkp->len, + announce ? 0x03 : + delete ? 0x01 : 0x00); + silc_buffer_free(pkp); + } + silc_hash_table_list_reset(&htl); + + return list; +} + +/* Sets the channel public keys into channel from the list of public keys. */ + +SilcStatus silc_server_set_channel_pk_list(SilcServer server, + SilcSocketConnection sender, + SilcChannelEntry channel, + const unsigned char *pklist, + SilcUInt32 pklist_len) +{ + SilcUInt16 argc; + SilcArgumentPayload args; + unsigned char *chpk; + SilcUInt32 chpklen, type; + SilcStatus ret = SILC_STATUS_OK; + + SILC_LOG_DEBUG(("Setting channel public keys list")); + + if (!pklist || pklist_len < 2) + return SILC_STATUS_ERR_NOT_ENOUGH_PARAMS; + + /* Get the argument from the Argument List Payload */ + SILC_GET16_MSB(argc, pklist); + args = silc_argument_payload_parse(pklist + 2, pklist_len - 2, argc); + if (!args) + return SILC_STATUS_ERR_NOT_ENOUGH_PARAMS; + + /* Process the public keys one by one */ + chpk = silc_argument_get_first_arg(args, &type, &chpklen); + + /* If announcing keys and we have them set already, do not allow this */ + if (chpk && type == 0x03 && channel->channel_pubkeys && + server->server_type == SILC_ROUTER && + sender != SILC_PRIMARY_ROUTE(server)) { + SILC_LOG_DEBUG(("Channel public key list set already, enforce our list")); + silc_argument_payload_free(args); + return SILC_STATUS_ERR_OPERATION_ALLOWED; + } + + /* If we are normal server and receive announcement list and we already + have keys set, we replace the old list with the announced one. */ + if (chpk && type == 0x03 && channel->channel_pubkeys && + server->server_type != SILC_ROUTER) { + SilcBuffer sidp; + unsigned char mask[4]; + + SILC_LOG_DEBUG(("Router enforces its list, remove old list")); + silc_hash_table_free(channel->channel_pubkeys); + channel->channel_pubkeys = NULL; + + /* Send notify that removes the old list */ + sidp = silc_id_payload_encode(server->id, SILC_ID_SERVER); + SILC_PUT32_MSB((channel->mode & (~SILC_CHANNEL_MODE_CHANNEL_AUTH)), mask); + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE, + SILC_NOTIFY_TYPE_CMODE_CHANGE, 7, + sidp->data, sidp->len, + mask, 4, + channel->cipher, + channel->cipher ? + strlen(channel->cipher) : 0, + channel->hmac_name, + channel->hmac_name ? + strlen(channel->hmac_name) : 0, + channel->passphrase, + channel->passphrase ? + strlen(channel->passphrase) : 0, + NULL, 0, NULL, 0); + silc_buffer_free(sidp); + } + + while (chpk) { + if (type == 0x03) + type = 0x00; + ret = silc_server_process_channel_pk(server, channel, type, + chpk, chpklen); + if (ret != SILC_STATUS_OK) + break; + chpk = silc_argument_get_next_arg(args, &type, &chpklen); + } + + silc_argument_payload_free(args); + return ret; +} + +/* Verifies the Authentication Payload `auth' with one of the public keys + on the `channel' public key list. */ + +bool silc_server_verify_channel_auth(SilcServer server, + SilcChannelEntry channel, + SilcClientID *client_id, + const unsigned char *auth, + SilcUInt32 auth_len) +{ + SilcAuthPayload ap; + SilcPublicKey chpk; + unsigned char *pkhash; + SilcUInt32 pkhash_len; + bool ret = FALSE; + + SILC_LOG_DEBUG(("Verifying channel authentication")); + + if (!auth || !auth_len || !channel->channel_pubkeys) + return FALSE; + + /* Get the hash from the auth data which tells us what public key we + must use in verification. */ + + ap = silc_auth_payload_parse(auth, auth_len); + if (!ap) + return FALSE; + + pkhash = silc_auth_get_public_data(ap, &pkhash_len); + if (pkhash_len < 128) + goto out; + + /* Find the public key with the hash */ + if (!silc_hash_table_find(channel->channel_pubkeys, pkhash, + NULL, (void **)&chpk)) { + SILC_LOG_DEBUG(("Public key not found in channel public key list")); + goto out; + } + + /* Verify the signature */ + if (!silc_auth_verify(ap, SILC_AUTH_PUBLIC_KEY, (void *)chpk, 0, + server->sha1hash, client_id, SILC_ID_CLIENT)) { + SILC_LOG_DEBUG(("Authentication failed")); + goto out; + } + + ret = TRUE; + + out: + silc_auth_payload_free(ap); + return ret; +}