+
+/* This function is used to send the notify packets and motd to the
+ incoming client connection. */
+
+void silc_server_send_connect_notifys(SilcServer server,
+ SilcPacketStream sock,
+ SilcClientEntry client)
+{
+ SilcCipher key;
+
+ SILC_LOG_DEBUG(("Send welcome notifys"));
+
+ /* Send some nice info to the client */
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Welcome to the SILC Network %s",
+ client->username));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your host is %s, running version %s",
+ server->server_name, server_version));
+
+ if (server->server_type == SILC_ROUTER) {
+ 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,
+ 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,
+ (server->standalone ? 0 :
+ !server->stat.routers ? 1 :
+ server->stat.routers)));
+ }
+
+ if (server->stat.cell_clients && server->stat.cell_servers + 1)
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("There are %d clients on %d servers in our cell",
+ server->stat.cell_clients,
+ 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_channels,
+ server->stat.my_servers,
+ server->stat.my_routers));
+ } else {
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("I have %d clients and %d channels formed",
+ server->stat.my_clients,
+ server->stat.my_channels));
+ }
+
+ if (server->stat.server_ops || server->stat.router_ops)
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("There are %d server operators and %d router "
+ "operators online",
+ server->stat.server_ops,
+ server->stat.router_ops));
+ if (server->stat.my_router_ops + server->stat.my_server_ops)
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("I have %d operators online",
+ server->stat.my_router_ops +
+ server->stat.my_server_ops));
+
+ silc_packet_get_keys(sock, &key, NULL, NULL, NULL);
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your connection is secured with %s cipher, "
+ "key length %d bits",
+ silc_cipher_get_name(key),
+ silc_cipher_get_key_len(key)));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your current nickname is %s",
+ client->nickname));
+
+ /* Send motd */
+ silc_server_send_motd(server, sock);
+}
+
+/* Kill the client indicated by `remote_client' sending KILLED notify
+ to the client, to all channels client has joined and to primary
+ router if needed. The killed client is also removed from all channels. */
+
+void silc_server_kill_client(SilcServer server,
+ SilcClientEntry remote_client,
+ const char *comment,
+ void *killer_id,
+ SilcIdType killer_id_type)
+{
+ SilcBuffer killed, killer;
+
+ 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
+ to our primary router and then directly to the client who is being
+ killed right now. */
+
+ killed = silc_id_payload_encode(remote_client->id, SILC_ID_CLIENT);
+ killer = silc_id_payload_encode(killer_id, killer_id_type);
+
+ /* 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,
+ remote_client, SILC_NOTIFY_TYPE_KILLED,
+ 3, killed->data, silc_buffer_len(killed),
+ comment, comment ? strlen(comment) : 0,
+ killer->data, silc_buffer_len(killer));
+
+ /* Send KILLED notify to primary route */
+ silc_server_send_notify_killed(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), remote_client->id,
+ comment, killer_id, killer_id_type);
+
+ /* 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 :
+ remote_client->router->connection, FALSE,
+ 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,
+ NULL, TRUE, TRUE);
+
+ /* Remove the client entry, If it is locally connected then we will also
+ disconnect the client here */
+ if (remote_client->connection) {
+ /* Remove locally conneted client */
+ SilcPacketStream sock = remote_client->connection;
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_close_connection(server, sock);
+ } else {
+ /* Update statistics */
+ server->stat.clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+ /* Remove client's public key from repository, this will free it too. */
+ if (remote_client->data.public_key) {
+ silc_skr_del_public_key(server->repository,
+ remote_client->data.public_key, remote_client);
+ remote_client->data.public_key = NULL;
+ }
+
+ if (SILC_IS_LOCAL(remote_client)) {
+ server->stat.my_clients--;
+ silc_schedule_task_del_by_context(server->schedule, remote_client);
+ }
+
+ /* Remove remote client */
+ silc_idlist_del_data(remote_client);
+ 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_buffer_free(killer);
+ silc_buffer_free(killed);
+}
+
+typedef struct {
+ SilcServer server;
+ SilcClientEntry client;
+ SilcNotifyType notify;
+ const char *new_nick;
+} WatcherNotifyContext;
+
+static void
+silc_server_check_watcher_list_foreach(void *key, void *context,
+ void *user_context)
+{
+ WatcherNotifyContext *notify = user_context;
+ SilcClientEntry entry = context;
+ SilcPacketStream sock;
+
+ if (!context)
+ return;
+
+ if (entry == notify->client)
+ return;
+
+ sock = silc_server_get_client_route(notify->server, NULL, 0, entry->id,
+ NULL, NULL);
+ if (sock) {
+ SILC_LOG_DEBUG(("Sending WATCH notify to %s",
+ silc_id_render(entry->id, SILC_ID_CLIENT)));
+
+ /* Send the WATCH notify */
+ silc_server_send_notify_watch(notify->server, sock, entry,
+ notify->client,
+ notify->new_nick ? notify->new_nick :
+ (const char *)notify->client->nickname,
+ notify->notify,
+ notify->client->data.public_key);
+ }
+}
+
+/* This function checks whether the `client' nickname and/or 'client'
+ public key is being watched by someone, and notifies the watcher of the
+ notify change of notify type indicated by `notify'. */
+
+SilcBool silc_server_check_watcher_list(SilcServer server,
+ SilcClientEntry client,
+ const char *new_nick,
+ SilcNotifyType notify)
+{
+ unsigned char hash[16];
+ WatcherNotifyContext n;
+
+ SILC_LOG_DEBUG(("Checking watcher list %s",
+ client->nickname ? client->nickname : (unsigned char *)""));
+
+ /* If the watching is rejected by the client do nothing */
+ if (client->mode & SILC_UMODE_REJECT_WATCHING)
+ return FALSE;
+
+ /* Make hash from the nick, or take it from Client ID */
+ if (client->nickname) {
+ unsigned char *nickc;
+ nickc = silc_identifier_check(client->nickname, strlen(client->nickname),
+ SILC_STRING_UTF8, 128, NULL);
+ if (!nickc)
+ return FALSE;
+ silc_hash_make(server->md5hash, nickc, strlen(nickc), hash);
+ silc_free(nickc);
+ } else {
+ memset(hash, 0, sizeof(hash));
+ memcpy(hash, client->id->hash, sizeof(client->id->hash));
+ }
+
+ n.server = server;
+ n.client = client;
+ n.new_nick = new_nick;
+ n.notify = notify;
+
+ /* Send notify to all watchers watching this nickname */
+ silc_hash_table_find_foreach(server->watcher_list, hash,
+ silc_server_check_watcher_list_foreach, &n);
+
+ /* Send notify to all watchers watching this public key */
+ if (client->data.public_key)
+ silc_hash_table_find_foreach(server->watcher_list_pk,
+ client->data.public_key,
+ silc_server_check_watcher_list_foreach,
+ &n);
+
+ return TRUE;
+}
+
+/* Remove the `client' from watcher list. After calling this the `client'
+ is not watching any nicknames. */
+
+SilcBool silc_server_del_from_watcher_list(SilcServer server,
+ SilcClientEntry client)
+{
+ SilcHashTableList htl;
+ void *key;
+ SilcClientEntry entry;
+ SilcBool found = FALSE;
+
+ silc_hash_table_list(server->watcher_list, &htl);
+ while (silc_hash_table_get(&htl, &key, (void *)&entry)) {
+ if (entry == client) {
+ silc_hash_table_del_by_context(server->watcher_list, key, client);
+
+ if (client->id)
+ SILC_LOG_DEBUG(("Removing %s from WATCH list",
+ silc_id_render(client->id, SILC_ID_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, key, NULL, NULL))
+ silc_free(key);
+
+ found = TRUE;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ silc_hash_table_list(server->watcher_list_pk, &htl);
+ while (silc_hash_table_get(&htl, &key, (void *)&entry)) {
+ if (entry == client) {
+ silc_hash_table_del_by_context(server->watcher_list_pk, key, client);
+
+ if (client->id)
+ SILC_LOG_DEBUG(("Removing %s from WATCH list",
+ silc_id_render(client->id, SILC_ID_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, key, NULL, NULL))
+ silc_pkcs_public_key_free(key);
+
+ found = TRUE;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ return found;
+}
+
+/* Force the client indicated by `chl' to change the channel user mode
+ on channel indicated by `channel' to `forced_mode'. */
+
+SilcBool silc_server_force_cumode_change(SilcServer server,
+ SilcPacketStream sock,
+ SilcChannelEntry channel,
+ SilcChannelClientEntry chl,
+ SilcUInt32 forced_mode)
+{
+ SilcBuffer idp1, idp2;
+ unsigned char cumode[4];
+
+ SILC_LOG_DEBUG(("Enforcing sender to change mode"));
+
+ if (sock)
+ silc_server_send_notify_cumode(server, sock, FALSE, channel, forced_mode,
+ server->id, SILC_ID_SERVER,
+ chl->client->id, NULL);
+
+ 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, TRUE,
+ SILC_NOTIFY_TYPE_CUMODE_CHANGE,
+ 3, idp1->data, silc_buffer_len(idp1),
+ cumode, sizeof(cumode),
+ idp2->data, silc_buffer_len(idp2));
+ silc_buffer_free(idp1);
+ silc_buffer_free(idp2);
+
+ return TRUE;
+}
+
+/* This function can be used to match the invite and ban lists. */
+
+SilcBool silc_server_inviteban_match(SilcServer server, SilcHashTable list,
+ SilcUInt8 type, void *check)
+{
+ unsigned char *tmp = NULL;
+ SilcUInt32 len = 0;
+ SilcHashTableList htl;
+ SilcBuffer entry, idp = NULL, pkp = NULL;
+ SilcBool ret = FALSE;
+ void *t;
+
+ SILC_LOG_DEBUG(("Matching invite/ban"));
+
+ if (type < 1 || type > 3 || !check)
+ return FALSE;
+
+ if (type == 1) {
+ tmp = strdup((char *)check);
+ if (!tmp)
+ return FALSE;
+ }
+ if (type == 2) {
+ pkp = silc_public_key_payload_encode(check);
+ if (!pkp)
+ return FALSE;
+ tmp = pkp->data;
+ len = silc_buffer_len(pkp);
+ }
+ if (type == 3) {
+ idp = silc_id_payload_encode(check, SILC_ID_CLIENT);
+ if (!idp)
+ return FALSE;
+ tmp = idp->data;
+ len = silc_buffer_len(idp);
+ }
+
+ /* Compare the list */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&t, (void *)&entry)) {
+ if (type == SILC_PTR_TO_32(t)) {
+ if (type == 1) {
+ if (silc_string_match(entry->data, tmp)) {
+ ret = TRUE;
+ break;
+ }
+ } else if (silc_buffer_len(entry) == len &&
+ !memcmp(entry->data, tmp, len)) {
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ if (type == 1)
+ silc_free(tmp);
+ silc_buffer_free(idp);
+ silc_buffer_free(pkp);
+ return ret;
+}
+
+/* Process invite or ban information */
+
+SilcBool silc_server_inviteban_process(SilcServer server,
+ SilcHashTable list,
+ SilcUInt8 action,
+ SilcArgumentPayload args)
+{
+ unsigned char *tmp;
+ SilcUInt32 type, len;
+ void *ptype;
+ SilcBuffer tmp2;
+ SilcHashTableList htl;
+
+ SILC_LOG_DEBUG(("Processing invite/ban for %s action",
+ action == 0x01 ? "DEL" : "ADD"));
+
+ /* Add the information to invite list */
+ 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);
+ while (tmp) {
+ if (type == 1) {
+ /* Check validity of the string. Actually we should parse the
+ whole string and verify all components individually. */
+ if (!silc_utf8_valid(tmp, len) || !len) {
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ continue;
+ }
+ if (strchr(tmp, ',')) {
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ continue;
+ }
+
+ /* Check if the string is added already */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2)) {
+ if (SILC_PTR_TO_32(ptype) == 1 &&
+ silc_string_match(tmp2->data, tmp)) {
+ tmp = NULL;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ if (tmp) {
+ /* Add the string to hash table */
+ tmp2 = silc_buffer_alloc_size(len + 1);
+ if (tmp[len - 1] == ',')
+ tmp[len - 1] = '\0';
+ silc_buffer_put(tmp2, tmp, len);
+ silc_hash_table_add(list, (void *)1, tmp2);
+ }
+
+ } else if (type == 2) {
+ /* Public key. Check first if the public key is already on the
+ list and ignore it if it is, otherwise, add it to hash table. */
+ SilcPublicKey pk;
+
+ /* Verify validity of the public key */
+ if (!silc_public_key_payload_decode(tmp, len, &pk)) {
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ continue;
+ }
+ silc_pkcs_public_key_free(pk);
+
+ /* Check if the public key is in the list already */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2)) {
+ if (SILC_PTR_TO_32(ptype) == 2 && !memcmp(tmp2->data, tmp, len)) {
+ tmp = NULL;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ /* Add new public key to invite list */
+ if (tmp) {
+ tmp2 = silc_buffer_alloc_size(len);
+ silc_buffer_put(tmp2, tmp, len);
+ silc_hash_table_add(list, (void *)2, tmp2);
+ }
+
+ } else if (type == 3) {
+ /* Client ID */
+
+ /* Check if the ID is in the list already */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2)) {
+ if (SILC_PTR_TO_32(ptype) == 3 && !memcmp(tmp2->data, tmp, len)) {
+ tmp = NULL;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ /* Add new Client ID to invite list */
+ if (tmp) {
+ tmp2 = silc_buffer_alloc_size(len);
+ silc_buffer_put(tmp2, tmp, len);
+ silc_hash_table_add(list, (void *)3, tmp2);
+ }
+ }
+
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ }
+ }
+
+ /* Delete information to invite list */
+ if (action == 0x01 && list) {
+ /* Now delete the arguments from invite list */
+ tmp = silc_argument_get_first_arg(args, &type, &len);
+ while (tmp) {
+ if (type == 1) {
+ /* Check validity of the string. Actually we should parse the
+ whole string and verify all components individually. */
+ if (!silc_utf8_valid(tmp, len)) {
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ continue;
+ }
+ if (strchr(tmp, ',')) {
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ continue;
+ }
+
+ /* Delete from the list */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2)) {
+ if (SILC_PTR_TO_32(ptype) == 1 &&
+ silc_string_match(tmp2->data, tmp)) {
+ silc_hash_table_del_by_context(list, (void *)1, tmp2);
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ } else if (type == 2) {
+ /* Public key. */
+ SilcPublicKey pk;
+
+ /* Verify validity of the public key */
+ if (!silc_public_key_payload_decode(tmp, len, &pk)) {
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ continue;
+ }
+ silc_pkcs_public_key_free(pk);
+
+ /* Delete from the invite list */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2)) {
+ if (SILC_PTR_TO_32(ptype) == 2 && !memcmp(tmp2->data, tmp, len)) {
+ silc_hash_table_del_by_context(list, (void *)2, tmp2);
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ } else if (type == 3) {
+ /* Client ID */
+
+ /* Delete from the invite list */
+ silc_hash_table_list(list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2)) {
+ if (SILC_PTR_TO_32(ptype) == 3 && !memcmp(tmp2->data, tmp, len)) {
+ silc_hash_table_del_by_context(list, (void *)3, tmp2);
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ }
+
+ tmp = silc_argument_get_next_arg(args, &type, &len);
+ }
+ }
+
+ return TRUE;
+}
+
+/* Destructor for invite and ban list entrys */
+
+void silc_server_inviteban_destruct(void *key, void *context,
+ void *user_context)
+{
+ silc_buffer_free(context);
+}
+
+/* Creates connections accoring to configuration. */
+
+void silc_server_create_connections(SilcServer server)
+{
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_to_router);
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router, server, 1, 0);
+}
+
+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_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,
+ SilcBool announce,
+ SilcBool 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_public_key_payload_encode(pk);
+ if (!pkp)
+ continue;
+ list = silc_argument_payload_encode_one(list, pkp->data,
+ silc_buffer_len(pkp),
+ 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,
+ SilcPacketStream 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], ulimit[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);
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ SILC_PUT32_MSB(channel->user_limit, ulimit);
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_CMODE_CHANGE, 8,
+ sidp->data, silc_buffer_len(sidp),
+ 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,
+ (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL),
+ (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 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. */
+
+SilcBool 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;
+ SilcBool 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;
+}