+
+ 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;