From 3ef234937ec402fb77006783624375ef61ffa65d Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Fri, 9 Mar 2001 00:22:35 +0000 Subject: [PATCH] updates. --- CHANGES | 163 ++ TODO | 31 +- apps/silc/client_ops.c | 18 + apps/silc/client_ops.h | 6 +- apps/silc/clientconfig.c | 146 +- apps/silc/clientconfig.h | 3 + apps/silc/local_command.c | 1 + apps/silc/silc.c | 1 + apps/silc/testi.conf | 12 + apps/silc/testi2.conf | 6 + apps/silcd/command.c | 29 +- apps/silcd/command_reply.c | 7 +- apps/silcd/idlist.c | 3 +- apps/silcd/idlist.h | 8 +- apps/silcd/packet_receive.c | 208 ++- apps/silcd/packet_receive.h | 6 + apps/silcd/packet_send.c | 42 + apps/silcd/packet_send.h | 10 + apps/silcd/protocol.c | 44 +- apps/silcd/protocol.h | 9 + apps/silcd/server.c | 140 +- apps/silcd/server.h | 2 + apps/silcd/server_internal.h | 9 + apps/silcd/serverconfig.c | 222 +-- apps/silcd/serverconfig.h | 3 + apps/silcd/testi2.conf | 16 +- doc/draft-riikonen-silc-ke-auth-01.nroff | 43 +- doc/draft-riikonen-silc-pp-01.nroff | 16 +- doc/draft-riikonen-silc-spec-01.nroff | 66 +- doc/example_silc.conf | 14 +- doc/example_silcd.conf | 16 +- lib/silcclient/Makefile.am | 4 + lib/silcclient/README | 2 +- lib/silcclient/client.c | 1843 ++-------------------- lib/silcclient/client.h | 61 +- lib/silcclient/client_channel.c | 368 +++++ lib/silcclient/client_internal.h | 72 + lib/silcclient/client_keyagr.c | 630 ++++++++ lib/silcclient/client_notify.c | 648 ++++++++ lib/silcclient/client_prvmsg.c | 538 +++++++ lib/silcclient/command.c | 3 +- lib/silcclient/command_reply.c | 30 +- lib/silcclient/idlist.c | 4 +- lib/silcclient/idlist.h | 2 + lib/silcclient/protocol.c | 125 +- lib/silcclient/protocol.h | 24 +- lib/silcclient/silcapi.h | 75 +- lib/silccore/silcauth.c | 2 +- lib/silccore/silcchannel.c | 112 +- lib/silccore/silcchannel.h | 12 +- lib/silccore/silcmode.h | 3 +- lib/silccore/silcpacket.c | 22 +- lib/silccore/silcprotocol.c | 1 + lib/silccrypt/silccipher.c | 23 +- lib/silccrypt/silccipher.h | 9 +- lib/silccrypt/silchash.c | 21 +- lib/silccrypt/silchash.h | 1 + lib/silccrypt/silchmac.c | 221 ++- lib/silccrypt/silchmac.h | 79 +- lib/silccrypt/silcpkcs.c | 3 + lib/silcske/payload.c | 22 +- lib/silcske/payload_internal.h | 3 + lib/silcske/silcske.c | 88 +- lib/silcske/silcske.h | 3 +- lib/silcske/silcske_status.h | 7 +- lib/silcutil/silcbuffmt.c | 6 + lib/silcutil/silctask.c | 23 + lib/silcutil/silctask.h | 4 + 68 files changed, 4106 insertions(+), 2288 deletions(-) create mode 100644 lib/silcclient/client_channel.c create mode 100644 lib/silcclient/client_internal.h create mode 100644 lib/silcclient/client_keyagr.c create mode 100644 lib/silcclient/client_notify.c create mode 100644 lib/silcclient/client_prvmsg.c diff --git a/CHANGES b/CHANGES index 9a644b76..47f3c148 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,166 @@ +Thu Mar 8 21:39:03 EET 2001 Pekka Riikonen + + * Added assert()s to buffer formatting and unformatting routines + to assert (if --enable-debug) when error occurs. Affected + file: lib/silcutil/silcbuffmt.c. + + * Changed to auto-reconnect to check whether the remote host is + router and register the re-connect timeout if it is. It used + to check that whether we are normal server, but router must do + auto-reconnect with another router as well. Affected file + silcd/server.c. + + * Removed the [] option from CMODE command as the cipher + name decides the key length, nowadays. See the defined ciphers + from the protocol specification. + + * Added [] option to the CMODE command to define the HMAC + for the channel. Added SILC_CMODE_HMAC channel mode. + + * Added [] option for the JOIN command so that user can + select which HMAC is used to compute the MACs of the channel + messages. + + * Added Hmac field to the Channel Message Payload. The integrity + of plaintext channel messages are now protected by computing + MAC of the message and attaching the MAC to the payload. The + MAC is not encrypted. Now, it is clear that this causes some + overhead to the size of the packet but rationale for this is that + now the receiver can verify whether the channel message decrypted + correctly and also when private keys are set for the channel the + receiver can decrypt the packet with several keys and check from + the MAC which key decrypted the message correctly. + + * Added silc_cipher_encrypt and silc_cipher_decrypt into the + lib/silccrypt/silccipher.[ch]. + + * Added silc_hash_len to return the digest length into the + lib/silcrypt/silchash.[ch]. + + * Rewrote parts of Silc Channel Payload interface in the + lib/silccore/silcchannel.[ch]. The encode function now also + encrypts the packet and parse function decrypts it. + + * Channel message delivery between routers was broken after the + channel key distribution was fixed earlier. The channel key + was used be to distributed to other routers as well which is not + allowed by the protocol. Now this is fixed and channel keys + really are cell specific and the channel message delivery between + routers comply with the protocol specification. + +Wed Mar 7 20:58:50 EET 2001 Pekka Riikonen + + * Fixed a minor formatting bug in the SKE's key material processing. + It actually might have processed the keys wrong way resulting + into wrong keys. + + * Redefined the mandatory HMAC algorithms and added new algorithms. + Added hmac-sha1-96 and hmac-md5-96 which are normal hmac-sha1 + and hmac-md5 truncated to 96 bits. The mandatory is now + hmac-sha1-96. Rest are optional (including the one that used + to be mandatory). Rationale for this is that the truncated HMAC + length is sufficient from security point of view and can actually + make the attack against the HMAC harder. Also, the truncated + HMAC causes less overhead to the packets. See the RFC2104 for + more information. + + * Added new [hmac] configuration section. The SKE used to use + the hash names (md5 and sha1) in the SKE proposal as HMCAS which + is of course wrong. The official names that must be proposed in + the SKE are the ones defined in the protocol specification + (hmac-sha1-96 for example). The user can configure any hmac + using any hash function configured in the [hash] section. At + least, the mandatory must be configured. + + Rewrote the HMAC interface in lib/silccrypt/silchmac.[ch]. + + * Added HMAC list to the SKE proposal list. It has now both + hash algorithm list and HMAC list. This makes the protocol + incompatible with previous versions. The SKE now seems to work + the way it is supposed to work, for the first time actually. + + * Defined plain Hash algorithms to the protocol specification. + Added sha1 and md5. + +Tue Mar 6 15:36:11 EET 2001 Pekka Riikonen + + * Implemented support for key agreement packets into the server. + Added functions silc_server_key_agreement and + silc_server_send_key_agreement. Other than these functions, + server has nothing to do with this packet. + + * Added support for private message key packets into the server. + Added functions silc_server_private_message_key and + silc_server_send_private_message_key. + + * Updated TODO. + + * Changed the silc_[client|server]_protocol_ke_set_keys to be + called in the protocol's final callback instead in the END + protocol state. This makes a little more sense and in the same + time in client we can use the same protocol routines for normal + key exchange and to key agreement packet handling as well. + + * Added to both client's and server's KE protocol context the + SilcSKEKeyMaterial pointer to save the key material. We will + bring the key material to the protocol's final callback by doing + this. The final callback must free the key material. + + * Added SKE's packet_send callback into client's KE protocol + context so that the caller can choose what packet sending function + is used. This way we can use different packet sending when + doing normal SKE when doing key agreement packet handling (in + the key agreement packet handling we do not want to encrypt + the packets). + + * Implemented the responder side of the key agreement routines + in the client. The client can now bind to specified port and + accept incoming key negotiation. The key material is passed + to the application after the protocol is over. + + * Implemented the processing of incoming Key Agreement packet + in the client. Added function silc_client_key_agreement to + process the packet. + + * Implemented the intiator side of the key agreement routines + in the client. The client can now initiate key agreement with + another remote client. The key material is passed to the + application after the protocol is over. + + * Created client_keyagr.c to include all the key agreement + routines. + + * Added macro SILC_TASK_CALLBACK_GLOBAL which is equal to the + SILC_TASK_CALLBACK except that it is not static. + + * Created client_notify.c and moved the Notify packet handling + from the client.[ch] into that file. + + * Created client_prvmsg.c and moved all private message and + private message key routines from the client.[ch] into that file. + + * Create client_channel.c and moved all channel message and + channel private key routines from the client.[ch] into that file. + + * Changed silc_client_get_client_by_id_resolve to resolve with + WHOIS command instead of IDENTIFY command, in the file + lib/silclient/idlist.c. + +Mon Mar 5 18:39:49 EET 2001 Pekka Riikonen + + * Implemented the SKE's responder side to the Client library. + + * When FAILURE is received to the protocol do not trust it + blindly. Register a timeout to wait whether the remote closes + the connection as it should do it, only after that process the + actual failure. This was changed to both client and server. + + * Added client_internal.h to include some of the structures + there instead of client.h in lib/silcclient/. + + * Added function silc_task_unregister_by_callback to unregister + timeouts by the callback function. + Sat Mar 3 19:15:43 EET 2001 Pekka Riikonen * Some "Incomplete WHOIS info" errors has been appearing on the diff --git a/TODO b/TODO index c72f3504..c408e032 100644 --- a/TODO +++ b/TODO @@ -75,14 +75,6 @@ TODO In SILC Client Library message sending. I guess the logic is done in server side but is missing from client. - o Private message key setting is missing and must be implemented. - Currently private messages are encrypted with session keys. This - is required by the protocol. - - o Channel private key setting is missing and must be implemented. - Currently there cannot be private keys for channels. Normal channel - keys (generated by server) are used. This is required by the protocol. - o I guess, public key authentication (when connecting to a server) is not working currently. It is just matter of loading the keys from file and using them (see corresponding code in server, it should @@ -91,17 +83,20 @@ TODO In SILC Client Library o Connection Authentication request resolving is missing and must be done. This is required by the protocol. - o Move ssh_client_notify_by_server to its own file (like notify.[ch]). - - o Key Exchange protocol's responder side is missing from client. - Generally it is possible for the client to be responder so it should - be implemented (See corresponding code from server). Error handling - in the KE protocol is also in pretty bad shape in client. - TODO In SILC Server =================== + o Packet processing can be made faster. All packet function in the + packet_receive.c has same prototypes. Instead of calling those from + huge switch() make a table of callback functions that can be called + directly by the packet type. + + o silc_server_send_key_agreement and silc_server_send_private_message_key + are one and same function (also silc_server_send_private_message is + almost same function). These should be unified to one generic named + function and use that. + o DNS/IP lookup blocks the server. This must be fixed. Check the resolver stuff (resolver(3), resolver(5)). Either we have to do the own resolver stuff (through scheduler, if possible without writing @@ -158,12 +153,6 @@ TODO In SILC Libraries not in distribution), but it is not used yet, and it requires some tweaking on the Makefiles (we want static lib not shared). - o Cipher API needs to be made more consistent. Some parts of the - code generated with current Cipher API looks really bad. Same - is with PKCS API, even worse actually. They need to be made - cleaner. Introducing silc_cipher_encrypt/decrypt/set_key etc. - functions (I actually don't understand why have I left these un-done). - o SIM support for SILC PKCS API needs to made so that they could be used as SIM's. At the same time some work is required on prime generation as the way it is done now sucks. Read from code for diff --git a/apps/silc/client_ops.c b/apps/silc/client_ops.c index 48ba1152..acf3186a 100644 --- a/apps/silc/client_ops.c +++ b/apps/silc/client_ops.c @@ -632,6 +632,23 @@ void silc_failure(SilcClient client, SilcClientConnection conn, } +/* Asks whether the user would like to perform the key agreement protocol. + This is called after we have received an key agreement packet or an + reply to our key agreement packet. This returns TRUE if the user wants + the library to perform the key agreement protocol and FALSE if it is not + desired (application may start it later by calling the function + silc_client_perform_key_agreement). */ + +int silc_key_agreement(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, char *hostname, + int port, + SilcKeyAgreementCallback *completion, + void **context) +{ + + return FALSE; +} + /* SILC client operations */ SilcClientOperations ops = { say: silc_say, @@ -646,4 +663,5 @@ SilcClientOperations ops = { verify_server_key: silc_verify_server_key, ask_passphrase: silc_ask_passphrase, failure: silc_failure, + key_agreement: silc_key_agreement, }; diff --git a/apps/silc/client_ops.h b/apps/silc/client_ops.h index 7d58937f..2dc68ce1 100644 --- a/apps/silc/client_ops.h +++ b/apps/silc/client_ops.h @@ -49,5 +49,9 @@ int silc_get_auth_method(SilcClient client, SilcClientConnection conn, unsigned int *auth_data_len); void silc_failure(SilcClient client, SilcClientConnection conn, SilcProtocol protocol, void *failure); - +int silc_key_agreement(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, char *hostname, + int port, + SilcKeyAgreementCallback *completion, + void **context); #endif diff --git a/apps/silc/clientconfig.c b/apps/silc/clientconfig.c index 65d9b275..ed7e4b7a 100644 --- a/apps/silc/clientconfig.c +++ b/apps/silc/clientconfig.c @@ -32,6 +32,8 @@ SilcClientConfigSection silc_client_config_sections[] = { SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 2 }, { "[hash]", SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 }, + { "[hmac]", + SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC, 3 }, { "[connection]", SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 }, { "[commands]", @@ -399,6 +401,57 @@ int silc_client_config_parse_lines(SilcClientConfig config, check = TRUE; break; + case SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC: + + if (!config->hmac) { + config->hmac = silc_calloc(1, sizeof(*config->hmac)); + config->hmac->next = NULL; + config->hmac->prev = NULL; + } else { + if (!config->hmac->next) { + config->hmac->next = + silc_calloc(1, sizeof(*config->hmac->next)); + config->hmac->next->next = NULL; + config->hmac->next->prev = config->hmac; + config->hmac = config->hmac->next; + } + } + + /* Get HMAC name */ + ret = silc_config_get_token(line, &config->hmac->alg_name); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: HMAC name not defined\n", + config->filename, pc->linenum); + break; + } + + /* Get Hash function name */ + ret = silc_config_get_token(line, &config->hmac->sim_name); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: Hash function name not defined\n", + config->filename, pc->linenum); + break; + } + + /* Get MAC length */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n", + config->filename, pc->linenum); + break; + } + config->hmac->key_len = atoi(tmp); + silc_free(tmp); + + check = TRUE; + break; + case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION: if (!config->conns) { @@ -513,6 +566,8 @@ int silc_client_config_parse_lines(SilcClientConfig config, config->pkcs = config->pkcs->prev; while (config->hash_func && config->hash_func->prev) config->hash_func = config->hash_func->prev; + while (config->hmac && config->hmac->prev) + config->hmac = config->hmac->prev; while (config->conns && config->conns->prev) config->conns = config->conns->prev; while (config->commands && config->commands->prev) @@ -642,7 +697,7 @@ void silc_client_config_register_pkcs(SilcClientConfig config) } } -/* Registers configured hash functions. These can then be allocated by the +/* Registers configured hash funtions. These can then be allocated by the client when needed. */ void silc_client_config_register_hashfuncs(SilcClientConfig config) @@ -655,74 +710,57 @@ void silc_client_config_register_hashfuncs(SilcClientConfig config) alg = config->hash_func; while(alg) { - if (!alg->sim_name) { - /* Hash module is supposed to be built in. Nothing to be done - here except to test that the hash function really is built in. */ - SilcHash tmp = NULL; - - if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) { - SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name)); + if (!silc_hash_is_supported(alg->alg_name)) { + SILC_LOG_ERROR(("Unsupported hash function `%s'", + alg->alg_name)); silc_client_stop(client); exit(1); } - silc_free(tmp); - -#ifdef SILC_SIM - } else { - /* Load (try at least) the hash SIM module */ - SilcHashObject hash; - SilcSimContext *sim; + } + alg = alg->next; + } +} - memset(&hash, 0, sizeof(hash)); - hash.name = alg->alg_name; - hash.block_len = alg->block_len; - hash.hash_len = alg->key_len; +/* Registers configured HMACs. These can then be allocated by the + client when needed. */ - sim = silc_sim_alloc(); - sim->type = SILC_SIM_HASH; - sim->libname = alg->sim_name; +void silc_client_config_register_hmacs(SilcClientConfig config) +{ + SilcClientConfigSectionAlg *alg; + SilcClientInternal app = (SilcClientInternal)config->client; + SilcClient client = app->client; - if ((silc_sim_load(sim))) { - hash.init = - silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, - SILC_HASH_SIM_INIT)); - SILC_LOG_DEBUG(("init=%p", hash.init)); - hash.update = - silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, - SILC_HASH_SIM_UPDATE)); - SILC_LOG_DEBUG(("update=%p", hash.update)); - hash.final = - silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, - SILC_HASH_SIM_FINAL)); - SILC_LOG_DEBUG(("final=%p", hash.final)); - hash.context_len = - silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, - SILC_HASH_SIM_CONTEXT_LEN)); - SILC_LOG_DEBUG(("context_len=%p", hash.context_len)); + SILC_LOG_DEBUG(("Registering configured HMACs")); - /* Put the SIM to the table of all SIM's in client */ - app->sim = silc_realloc(app->sim, - sizeof(*app->sim) * - (app->sim_count + 1)); - app->sim[app->sim_count] = sim; - app->sim_count++; - } else { - SILC_LOG_ERROR(("Error configuring hash functions")); - silc_client_stop(client); - exit(1); - } + if (!config->hmac) { + SILC_LOG_ERROR(("HMACs are not configured. SILC cannot work without " + "HMACs")); + silc_client_stop(client); + exit(1); + } - /* Register the cipher */ - silc_hash_register(&hash); -#endif + alg = config->hmac; + while(alg) { + SilcHmacObject hmac; + + if (!silc_hash_is_supported(alg->sim_name)) { + SILC_LOG_ERROR(("Unsupported hash function `%s'", + alg->sim_name)); + silc_client_stop(client); + exit(1); } + + /* Register the HMAC */ + memset(&hmac, 0, sizeof(hmac)); + hmac.name = alg->alg_name; + hmac.len = alg->key_len; + silc_hmac_register(&hmac); alg = alg->next; } } - SilcClientConfigSectionConnection * silc_client_config_find_connection(SilcClientConfig config, char *host, int port) diff --git a/apps/silc/clientconfig.h b/apps/silc/clientconfig.h index f2c7a4af..30221a7a 100644 --- a/apps/silc/clientconfig.h +++ b/apps/silc/clientconfig.h @@ -69,6 +69,7 @@ typedef struct { SilcClientConfigSectionAlg *cipher; SilcClientConfigSectionAlg *pkcs; SilcClientConfigSectionAlg *hash_func; + SilcClientConfigSectionAlg *hmac; SilcClientConfigSectionConnection *conns; SilcClientConfigSectionCommand *commands; } SilcClientConfigObject; @@ -81,6 +82,7 @@ typedef enum { SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, + SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC, SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND = 253, /* Special section */ } SilcClientConfigSectionType; @@ -117,6 +119,7 @@ void silc_client_config_setlogfiles(SilcClientConfig config); void silc_client_config_register_ciphers(SilcClientConfig config); void silc_client_config_register_pkcs(SilcClientConfig config); void silc_client_config_register_hashfuncs(SilcClientConfig config); +void silc_client_config_register_hmacs(SilcClientConfig config); SilcClientConfigSectionConnection * silc_client_config_find_connection(SilcClientConfig config, char *host, int port); diff --git a/apps/silc/local_command.c b/apps/silc/local_command.c index 6429bfc5..065da938 100644 --- a/apps/silc/local_command.c +++ b/apps/silc/local_command.c @@ -20,6 +20,7 @@ /* $Id$ */ #include "clientincludes.h" +#include "client_internal.h" /* Local commands. */ SilcClientCommand silc_local_command_list[] = diff --git a/apps/silc/silc.c b/apps/silc/silc.c index 3ebe26a0..bfb4ab72 100644 --- a/apps/silc/silc.c +++ b/apps/silc/silc.c @@ -275,6 +275,7 @@ SILC Secure Internet Live Conferencing, version %s\n", silc_client_config_register_ciphers(app->config); silc_client_config_register_pkcs(app->config); silc_client_config_register_hashfuncs(app->config); + silc_client_config_register_hmacs(app->config); /* Load public and private key */ if (silc_client_load_keys(silc) == FALSE) diff --git a/apps/silc/testi.conf b/apps/silc/testi.conf index 1289ea7c..2976768e 100644 --- a/apps/silc/testi.conf +++ b/apps/silc/testi.conf @@ -28,6 +28,18 @@ none:../lib/silcsim/modules/none.sim.so:0:0 md5::64:16 sha1::64:20 +# +# Configured HMAC functions. The hash function used in the HMAC must +# configured to the [hash] section. +# +# Format: :: +# +[hmac] +hmac-sha1-96:sha1:12 +hmac-md5-96:md5:12 +hmac-sha1:sha1:20 +hmac-md5:md5:16 + # # Configured PKCS. # diff --git a/apps/silc/testi2.conf b/apps/silc/testi2.conf index 64f3839f..b6935b7f 100644 --- a/apps/silc/testi2.conf +++ b/apps/silc/testi2.conf @@ -8,6 +8,12 @@ none:/home/silc/silc/lib/silcsim/modules/none.sim.so:0:0 md5::64:16 sha1::64:20 +[hmac] +hmac-sha1-96:sha1:12 +hmac-md5-96:md5:12 +hmac-sha1:sha1:20 +hmac-md5:md5:16 + #[pkcs] #rsa::1024 #dss::1024 diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 78b90087..779c0385 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -1937,7 +1937,7 @@ static void silc_server_command_join_channel(SilcServer server, 4, mode, 4, 5, tmp2, 4, 6, keyp->data, keyp->len, - 8, channel->topic, + 9, channel->topic, strlen(channel->topic)); } @@ -1982,7 +1982,7 @@ SILC_SERVER_CMD_FUNC(join) SilcServerCommandContext cmd = (SilcServerCommandContext)context; SilcServer server = cmd->server; int tmp_len; - char *tmp, *channel_name = NULL, *cipher = NULL; + char *tmp, *channel_name = NULL, *cipher, *hmac; SilcChannelEntry channel; unsigned int umode = 0; int created = FALSE; @@ -2020,8 +2020,9 @@ SILC_SERVER_CMD_FUNC(join) goto out; } - /* Get cipher name */ + /* Get cipher and hmac name */ cipher = silc_argument_get_arg_type(cmd->args, 4, NULL); + hmac = silc_argument_get_arg_type(cmd->args, 5, NULL); /* See if the channel exists */ channel = silc_idlist_find_channel_by_name(server->local_list, @@ -2046,7 +2047,7 @@ SILC_SERVER_CMD_FUNC(join) the channel by ourselves. */ if (server->standalone) { channel = silc_server_create_new_channel(server, server->id, cipher, - channel_name, TRUE); + hmac, channel_name, TRUE); umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); created = TRUE; @@ -2087,7 +2088,7 @@ SILC_SERVER_CMD_FUNC(join) if (!channel) { /* Channel really does not exist, create it */ channel = silc_server_create_new_channel(server, server->id, cipher, - channel_name, TRUE); + hmac, channel_name, TRUE); umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); created = TRUE; } @@ -2111,7 +2112,7 @@ SILC_SERVER_CMD_FUNC(join) if (!channel) { /* Channel really does not exist, create it */ channel = silc_server_create_new_channel(server, server->id, cipher, - channel_name, TRUE); + hmac, channel_name, TRUE); umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); created = TRUE; } @@ -2464,23 +2465,16 @@ SILC_SERVER_CMD_FUNC(cmode) if (mode_mask & SILC_CHANNEL_MODE_CIPHER) { if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) { /* Cipher to use protect the traffic */ - unsigned int key_len = 256; - char *cp; + unsigned int key_len; /* Get cipher */ tmp = silc_argument_get_arg_type(cmd->args, 8, NULL); if (!tmp) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - cp = strchr(tmp, ':'); - if (cp) { - key_len = atoi(cp); - *cp = '\0'; - } - /* XXX Duplicated code, make own function for this!! */ /* Delete old cipher and allocate the new one */ @@ -2490,10 +2484,7 @@ SILC_SERVER_CMD_FUNC(cmode) SILC_STATUS_ERR_UNKNOWN_ALGORITHM); goto out; } - - key_len /= 8; - if (key_len > 32) - key_len = 32; + key_len = silc_cipher_get_key_len(channel->channel_key) / 8; /* Re-generate channel key */ silc_server_create_channel_key(server, channel, key_len); diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 7086cc17..0034de11 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -378,7 +378,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join) SilcChannelEntry entry; unsigned int id_len, len; unsigned char *id_string; - char *channel_name, *tmp; + char *channel_name, *tmp, *hmac; unsigned int mode, created; SilcBuffer keyp; @@ -420,6 +420,9 @@ SILC_SERVER_CMD_REPLY_FUNC(join) if (!id) goto out; + /* Get hmac */ + hmac = silc_argument_get_arg_type(cmd->args, 10, NULL); + /* See whether we already have the channel. */ entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL); if (!entry) { @@ -432,7 +435,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join) /* Add the channel to our local list. */ entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), SILC_CHANNEL_MODE_NONE, id, - server->router, NULL); + server->router, NULL, hmac); if (!entry) { silc_free(id); goto out; diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index d8a747fc..b40ff6a6 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -561,7 +561,7 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id, SilcChannelEntry silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, SilcChannelID *id, SilcServerEntry router, - SilcCipher channel_key) + SilcCipher channel_key, char *hmac) { SilcChannelEntry channel; @@ -571,6 +571,7 @@ silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, channel->id = id; channel->router = router; channel->channel_key = channel_key; + channel->hmac = hmac ? strdup(hmac) : strdup("hmac-sha1-96"); silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, channel_list); diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index 444f9108..bbce9ea5 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -361,6 +361,11 @@ struct SilcClientEntryStruct { Current initial vector. Initial vector is received always along with the channel packet. By default this is filled with NULL. + char *hmac; + + HMAC of the channel. Server only saves the name of the HMAC as + it never actually needs to compute the MAC. + */ struct SilcChannelEntryStruct { char *channel_name; @@ -391,6 +396,7 @@ struct SilcChannelEntryStruct { unsigned char *key; unsigned int key_len; unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; + char *hmac; }; /* @@ -500,7 +506,7 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id, SilcChannelEntry silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, SilcChannelID *id, SilcServerEntry router, - SilcCipher channel_key); + SilcCipher channel_key, char *hmac); int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry); SilcChannelEntry silc_idlist_find_channel_by_name(SilcIDList id_list, char *name, diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index ef57bcd6..403c5928 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -617,14 +617,18 @@ void silc_server_private_message(SilcServer server, SILC_LOG_DEBUG(("Start")); + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT) + return; + if (!packet->dst_id) - goto err; + return; /* Decode destination Client ID */ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT); if (!id) { SILC_LOG_ERROR(("Could not decode destination Client ID, dropped")); - goto err; + return; } /* If the destination belongs to our server we don't have to route @@ -692,10 +696,103 @@ void silc_server_private_message(SilcServer server, return; } } +} + +/* Received private message key packet.. This packet is never for us. It is to + the client in the packet's destination ID. Sending of this sort of packet + equals sending private message, ie. it is sent point to point from + one client to another. */ + +void silc_server_private_message_key(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcClientID *id; + SilcServerEntry router; + SilcSocketConnection dst_sock; + SilcClientEntry client; + SilcIDListData idata; + + SILC_LOG_DEBUG(("Start")); + + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT) + return; + + if (!packet->dst_id) + return; + + /* Decode destination Client ID */ + id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT); + if (!id) { + SILC_LOG_ERROR(("Could not decode destination Client ID, dropped")); + return; + } + + /* If the destination belongs to our server we don't have to route + the message anywhere but to send it to the local destination. */ + client = silc_idlist_find_client_by_id(server->local_list, id, NULL); + if (client) { + /* It exists, now deliver the message to the destination */ + dst_sock = (SilcSocketConnection)client->connection; + + /* If we are router and the client has router then the client is in + our cell but not directly connected to us. */ + if (server->server_type == SILC_ROUTER && client->router) { + /* We are of course in this case the client's router thus the real + "router" of the client is the server who owns the client. Thus + we will send the packet to that server. */ + router = (SilcServerEntry)client->router; + idata = (SilcIDListData)router; + silc_server_send_private_message_key(server, router->connection, + idata->send_key, + idata->hmac, + packet); + return; + } + + /* Seems that client really is directly connected to us */ + idata = (SilcIDListData)client; + silc_server_send_private_message_key(server, dst_sock, + idata->send_key, + idata->hmac, packet); + return; + } + + /* Destination belongs to someone not in this server. If we are normal + server our action is to send the packet to our router. */ + if (server->server_type == SILC_SERVER && !server->standalone) { + router = server->router; + + /* Send to primary route */ + if (router) { + dst_sock = (SilcSocketConnection)router->connection; + idata = (SilcIDListData)router; + silc_server_send_private_message_key(server, dst_sock, + idata->send_key, + idata->hmac, packet); + } + return; + } + + /* We are router and we will perform route lookup for the destination + and send the packet to fastest route. */ + if (server->server_type == SILC_ROUTER && !server->standalone) { + /* Check first that the ID is valid */ + client = silc_idlist_find_client_by_id(server->global_list, id, NULL); + if (client) { + dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT); + router = (SilcServerEntry)dst_sock->user_data; + idata = (SilcIDListData)router; - err: - silc_server_send_error(server, sock, - "No such nickname: Private message not sent"); + /* Get fastest route and send packet. */ + if (router) + silc_server_send_private_message_key(server, dst_sock, + idata->send_key, + idata->hmac, packet); + return; + } + } } /* Processes incoming command reply packet. The command reply packet may @@ -1357,7 +1454,7 @@ void silc_server_new_channel(SilcServer server, sock->hostname)); silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, - server->router->connection, NULL); + server->router->connection, NULL, NULL); server->stat.channels++; } else { @@ -1381,7 +1478,7 @@ void silc_server_new_channel(SilcServer server, with the channel ID provided by the server. This creates a new key to the channel as well that we will send to the server. */ if (!channel) { - channel = silc_server_create_new_channel_with_id(server, NULL, + channel = silc_server_create_new_channel_with_id(server, NULL, NULL, channel_name, channel_id, FALSE); if (!channel) @@ -1512,3 +1609,100 @@ void silc_server_new_channel_list(SilcServer server, silc_buffer_free(buffer); silc_free(new); } + +/* Received key agreement packet. This packet is never for us. It is to + the client in the packet's destination ID. Sending of this sort of packet + equals sending private message, ie. it is sent point to point from + one client to another. */ + +void silc_server_key_agreement(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcClientID *id; + SilcServerEntry router; + SilcSocketConnection dst_sock; + SilcClientEntry client; + SilcIDListData idata; + + SILC_LOG_DEBUG(("Start")); + + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT) + return; + + if (!packet->dst_id) + return; + + /* Decode destination Client ID */ + id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT); + if (!id) { + SILC_LOG_ERROR(("Could not decode destination Client ID, dropped")); + return; + } + + /* If the destination belongs to our server we don't have to route + the message anywhere but to send it to the local destination. */ + client = silc_idlist_find_client_by_id(server->local_list, id, NULL); + if (client) { + /* It exists, now deliver the message to the destination */ + dst_sock = (SilcSocketConnection)client->connection; + + /* If we are router and the client has router then the client is in + our cell but not directly connected to us. */ + if (server->server_type == SILC_ROUTER && client->router) { + /* We are of course in this case the client's router thus the real + "router" of the client is the server who owns the client. Thus + we will send the packet to that server. */ + router = (SilcServerEntry)client->router; + idata = (SilcIDListData)router; + silc_server_send_key_agreement(server, router->connection, + idata->send_key, + idata->hmac, + packet); + return; + } + + /* Seems that client really is directly connected to us */ + idata = (SilcIDListData)client; + silc_server_send_key_agreement(server, dst_sock, + idata->send_key, + idata->hmac, packet); + return; + } + + /* Destination belongs to someone not in this server. If we are normal + server our action is to send the packet to our router. */ + if (server->server_type == SILC_SERVER && !server->standalone) { + router = server->router; + + /* Send to primary route */ + if (router) { + dst_sock = (SilcSocketConnection)router->connection; + idata = (SilcIDListData)router; + silc_server_send_key_agreement(server, dst_sock, + idata->send_key, + idata->hmac, packet); + } + return; + } + + /* We are router and we will perform route lookup for the destination + and send the packet to fastest route. */ + if (server->server_type == SILC_ROUTER && !server->standalone) { + /* Check first that the ID is valid */ + client = silc_idlist_find_client_by_id(server->global_list, id, NULL); + if (client) { + dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT); + router = (SilcServerEntry)dst_sock->user_data; + idata = (SilcIDListData)router; + + /* Get fastest route and send packet. */ + if (router) + silc_server_send_key_agreement(server, dst_sock, + idata->send_key, + idata->hmac, packet); + return; + } + } +} diff --git a/apps/silcd/packet_receive.h b/apps/silcd/packet_receive.h index 15d6b22f..cc450d77 100644 --- a/apps/silcd/packet_receive.h +++ b/apps/silcd/packet_receive.h @@ -32,6 +32,9 @@ void silc_server_notify_list(SilcServer server, void silc_server_private_message(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); +void silc_server_private_message_key(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); void silc_server_command_reply(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); @@ -63,5 +66,8 @@ void silc_server_remove_id(SilcServer server, void silc_server_remove_id_list(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); +void silc_server_key_agreement(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); #endif diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index 840fd68b..28228987 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -1258,3 +1258,45 @@ void silc_server_send_heartbeat(SilcServer server, silc_server_packet_send(server, sock, SILC_PACKET_HEARTBEAT, 0, NULL, 0, FALSE); } + +/* Routine used to send (relay, route) key agreement packets to some + destination. */ + +void silc_server_send_key_agreement(SilcServer server, + SilcSocketConnection dst_sock, + SilcCipher cipher, + SilcHmac hmac, + SilcPacketContext *packet) +{ + silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len); + silc_buffer_put(dst_sock->outbuf, packet->buffer->data, packet->buffer->len); + + /* Re-encrypt packet */ + silc_packet_encrypt(cipher, hmac, dst_sock->outbuf, packet->buffer->len); + + /* Send the packet */ + silc_server_packet_send_real(server, dst_sock, FALSE); +} + +/* Routine used to send (relay, route) private message key packets to some + destination. */ + +void silc_server_send_private_message_key(SilcServer server, + SilcSocketConnection dst_sock, + SilcCipher cipher, + SilcHmac hmac, + SilcPacketContext *packet) +{ + silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len); + silc_buffer_put(dst_sock->outbuf, packet->buffer->data, packet->buffer->len); + + /* Re-encrypt packet */ + silc_packet_encrypt(cipher, hmac, dst_sock->outbuf, packet->buffer->len); + + /* Send the packet */ + silc_server_packet_send_real(server, dst_sock, FALSE); +} diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h index 87b072f6..89daefe2 100644 --- a/apps/silcd/packet_send.h +++ b/apps/silcd/packet_send.h @@ -189,5 +189,15 @@ void silc_server_send_command(SilcServer server, unsigned int argc, ...); void silc_server_send_heartbeat(SilcServer server, SilcSocketConnection sock); +void silc_server_send_key_agreement(SilcServer server, + SilcSocketConnection dst_sock, + SilcCipher cipher, + SilcHmac hmac, + SilcPacketContext *packet); +void silc_server_send_private_message_key(SilcServer server, + SilcSocketConnection dst_sock, + SilcCipher cipher, + SilcHmac hmac, + SilcPacketContext *packet); #endif diff --git a/apps/silcd/protocol.c b/apps/silcd/protocol.c index ad86e001..ca091612 100644 --- a/apps/silcd/protocol.c +++ b/apps/silcd/protocol.c @@ -54,17 +54,17 @@ static void silc_server_protocol_ke_send_packet(SilcSKE ske, /* Sets the negotiated key material into use for particular connection. */ -static int silc_server_protocol_ke_set_keys(SilcSKE ske, - SilcSocketConnection sock, - SilcSKEKeyMaterial *keymat, - SilcCipher cipher, - SilcPKCS pkcs, - SilcHash hash, - int is_responder) +int silc_server_protocol_ke_set_keys(SilcSKE ske, + SilcSocketConnection sock, + SilcSKEKeyMaterial *keymat, + SilcCipher cipher, + SilcPKCS pkcs, + SilcHash hash, + SilcHmac hmac, + int is_responder) { SilcUnknownEntry conn_data; SilcIDListData idata; - SilcHash nhash; SILC_LOG_DEBUG(("Setting new key into use")); @@ -114,13 +114,12 @@ static int silc_server_protocol_ke_set_keys(SilcSKE ske, #endif /* Save HMAC key to be used in the communication. */ - if (!silc_hash_alloc(hash->hash->name, &nhash)) { + if (!silc_hmac_alloc(hmac->hmac->name, NULL, &idata->hmac)) { silc_cipher_free(idata->send_key); silc_cipher_free(idata->receive_key); silc_free(conn_data); return FALSE; } - silc_hmac_alloc(nhash, &idata->hmac); silc_hmac_set_key(idata->hmac, keymat->hmac_key, keymat->hmac_key_len); sock->user_data = (void *)conn_data; @@ -346,32 +345,25 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange) * End protocol */ SilcSKEKeyMaterial *keymat; - int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher, NULL); + int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher); int hash_len = ctx->ske->prop->hash->hash->hash_len; - /* Send Ok to the other end if we are responder. If we are - initiator we have sent this already. */ - if (ctx->responder == TRUE) - silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context); - /* Process the key material */ keymat = silc_calloc(1, sizeof(*keymat)); - silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len, - keymat); - - /* Take the new keys into use. */ - if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat, - ctx->ske->prop->cipher, - ctx->ske->prop->pkcs, - ctx->ske->prop->hash, - ctx->responder)) { + status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len, + keymat); + if (status != SILC_SKE_STATUS_OK) { protocol->state = SILC_PROTOCOL_STATE_ERROR; protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); silc_ske_free_key_material(keymat); return; } + ctx->keymat = keymat; - silc_ske_free_key_material(keymat); + /* Send Ok to the other end if we are responder. If we are initiator + we have sent this already. */ + if (ctx->responder == TRUE) + silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context); /* Unregister the timeout task since the protocol has ended. This was the timeout task to be executed if the protocol is diff --git a/apps/silcd/protocol.h b/apps/silcd/protocol.h index d6f54663..7b57da2d 100644 --- a/apps/silcd/protocol.h +++ b/apps/silcd/protocol.h @@ -45,6 +45,7 @@ typedef struct { SilcTask timeout_task; SilcPacketContext *packet; SilcSKE ske; + SilcSKEKeyMaterial *keymat; } SilcServerKEInternalContext; /* Internal context for connection authentication protocol */ @@ -82,5 +83,13 @@ typedef struct { /* Prototypes */ void silc_server_protocols_register(void); void silc_server_protocols_unregister(void); +int silc_server_protocol_ke_set_keys(SilcSKE ske, + SilcSocketConnection sock, + SilcSKEKeyMaterial *keymat, + SilcCipher cipher, + SilcPKCS pkcs, + SilcHash hash, + SilcHmac hmac, + int is_responder); #endif diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 5db18ef6..765f6be5 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -38,6 +38,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final); SILC_TASK_CALLBACK(silc_server_packet_process); SILC_TASK_CALLBACK(silc_server_packet_parse_real); SILC_TASK_CALLBACK(silc_server_timeout_remote); +SILC_TASK_CALLBACK(silc_server_failure_callback); /* Allocates a new SILC server object. This has to be done before the server can be used. After allocation one must call silc_server_init to initialize @@ -135,6 +136,7 @@ int silc_server_init(SilcServer server) silc_server_config_register_ciphers(server->config); silc_server_config_register_pkcs(server->config); silc_server_config_register_hashfuncs(server->config); + silc_server_config_register_hmacs(server->config); /* Initialize random number generator for the server. */ server->rng = silc_rng_alloc(); @@ -273,8 +275,6 @@ int silc_server_init(SilcServer server) is sent as argument for fast referencing in the future. */ silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, &newsocket); - if (!newsocket) - goto err0; server->sockets[sock[i]] = newsocket; @@ -693,6 +693,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { /* Error occured during protocol */ silc_protocol_free(protocol); + silc_ske_free_key_material(ctx->keymat); if (ctx->packet) silc_packet_context_free(ctx->packet); if (ctx->ske) @@ -700,13 +701,39 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) if (ctx->dest_id) silc_free(ctx->dest_id); silc_free(ctx); - if (sock) - sock->protocol = NULL; + silc_task_unregister_by_callback(server->timeout_queue, + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Key exchange failed"); return; } + /* We now have the key material as the result of the key exchange + protocol. Take the key material into use. Free the raw key material + as soon as we've set them into use. */ + if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat, + ctx->ske->prop->cipher, + ctx->ske->prop->pkcs, + ctx->ske->prop->hash, + ctx->ske->prop->hmac, + ctx->responder)) { + silc_protocol_free(protocol); + silc_ske_free_key_material(ctx->keymat); + if (ctx->packet) + silc_packet_context_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + if (ctx->dest_id) + silc_free(ctx->dest_id); + silc_free(ctx); + silc_task_unregister_by_callback(server->timeout_queue, + silc_server_failure_callback); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Key exchange failed"); + return; + } + silc_ske_free_key_material(ctx->keymat); + /* Allocate internal context for the authentication protocol. This is sent as context for the protocol. */ proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); @@ -1002,6 +1029,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { /* Error occured during protocol */ silc_protocol_free(protocol); + silc_ske_free_key_material(ctx->keymat); if (ctx->packet) silc_packet_context_free(ctx->packet); if (ctx->ske) @@ -1009,14 +1037,41 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) if (ctx->dest_id) silc_free(ctx->dest_id); silc_free(ctx); - if (sock) - sock->protocol = NULL; + silc_task_unregister_by_callback(server->timeout_queue, + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Key exchange failed"); server->stat.auth_failures++; return; } + /* We now have the key material as the result of the key exchange + protocol. Take the key material into use. Free the raw key material + as soon as we've set them into use. */ + if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat, + ctx->ske->prop->cipher, + ctx->ske->prop->pkcs, + ctx->ske->prop->hash, + ctx->ske->prop->hmac, + ctx->responder)) { + silc_protocol_free(protocol); + silc_ske_free_key_material(ctx->keymat); + if (ctx->packet) + silc_packet_context_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + if (ctx->dest_id) + silc_free(ctx->dest_id); + silc_free(ctx); + silc_task_unregister_by_callback(server->timeout_queue, + silc_server_failure_callback); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Key exchange failed"); + server->stat.auth_failures++; + return; + } + silc_ske_free_key_material(ctx->keymat); + /* Allocate internal context for the authentication protocol. This is sent as context for the protocol. */ proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); @@ -1081,6 +1136,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) silc_free(ctx); if (sock) sock->protocol = NULL; + silc_task_unregister_by_callback(server->timeout_queue, + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Authentication failed"); server->stat.auth_failures++; @@ -1192,6 +1249,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) silc_server_perform_heartbeat, server->timeout_queue); + silc_task_unregister_by_callback(server->timeout_queue, + silc_server_failure_callback); silc_protocol_free(protocol); if (ctx->packet) silc_packet_context_free(ctx->packet); @@ -1278,11 +1337,11 @@ SILC_TASK_CALLBACK(silc_server_packet_process) /* If the closed connection was our primary router connection the start re-connecting phase. */ - if (!server->standalone && server->server_type == SILC_SERVER && + if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && sock == server->router->connection) silc_task_register(server->timeout_queue, 0, silc_server_connect_to_router, - context, 0, 500000, + context, 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); @@ -1458,10 +1517,15 @@ void silc_server_packet_parse_type(SilcServer server, if (packet->flags & SILC_PACKET_FLAG_LIST) break; if (sock->protocol) { - /* XXX Audit the failure type */ - sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE; - sock->protocol->execute(server->timeout_queue, 0, - sock->protocol, sock->sock, 0, 0); + SilcServerFailureContext f; + f = silc_calloc(1, sizeof(*f)); + f->server = server; + f->sock = sock; + + /* We will wait 5 seconds to process this failure packet */ + silc_task_register(server->timeout_queue, sock->sock, + silc_server_failure_callback, (void *)f, 5, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); } break; @@ -1558,6 +1622,7 @@ void silc_server_packet_parse_type(SilcServer server, */ if (packet->flags & SILC_PACKET_FLAG_LIST) break; + silc_server_private_message_key(server, sock, packet); break; /* @@ -1747,6 +1812,16 @@ void silc_server_packet_parse_type(SilcServer server, break; break; + case SILC_PACKET_KEY_AGREEMENT: + /* + * Received heartbeat. + */ + SILC_LOG_DEBUG(("Key agreement packet")); + if (packet->flags & SILC_PACKET_FLAG_LIST) + break; + silc_server_key_agreement(server, sock, packet); + break; + default: SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type)); break; @@ -2231,6 +2306,7 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote) SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcServerID *router_id, char *cipher, + char *hmac, char *channel_name, int broadcast) { @@ -2242,6 +2318,8 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, if (!cipher) cipher = "aes-256-cbc"; + if (!hmac) + hmac = "hmac-sha1-96"; /* Allocate cipher */ if (!silc_cipher_alloc(cipher, &key)) @@ -2253,14 +2331,15 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, silc_id_create_channel_id(router_id, server->rng, &channel_id); entry = silc_idlist_add_channel(server->local_list, channel_name, SILC_CHANNEL_MODE_NONE, channel_id, - NULL, key); + NULL, key, hmac); if (!entry) { silc_free(channel_name); return NULL; } /* Now create the actual key material */ - silc_server_create_channel_key(server, entry, 32); + silc_server_create_channel_key(server, entry, + silc_cipher_get_key_len(key) / 8); /* Notify other routers about the new channel. We send the packet to our primary route. */ @@ -2279,6 +2358,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcChannelEntry silc_server_create_new_channel_with_id(SilcServer server, char *cipher, + char *hmac, char *channel_name, SilcChannelID *channel_id, int broadcast) @@ -2290,6 +2370,8 @@ silc_server_create_new_channel_with_id(SilcServer server, if (!cipher) cipher = "aes-256-cbc"; + if (!hmac) + hmac = "hmac-sha1-96"; /* Allocate cipher */ if (!silc_cipher_alloc(cipher, &key)) @@ -2300,14 +2382,15 @@ silc_server_create_new_channel_with_id(SilcServer server, /* Create the channel */ entry = silc_idlist_add_channel(server->local_list, channel_name, SILC_CHANNEL_MODE_NONE, channel_id, - NULL, key); + NULL, key, hmac); if (!entry) { silc_free(channel_name); return NULL; } /* Now create the actual key material */ - silc_server_create_channel_key(server, entry, 32); + silc_server_create_channel_key(server, entry, + silc_cipher_get_key_len(key) / 8); /* Notify other routers about the new channel. We send the packet to our primary route. */ @@ -2334,14 +2417,15 @@ void silc_server_create_channel_key(SilcServer server, unsigned int len; if (!channel->channel_key) - silc_cipher_alloc("aes-256-cbc", &channel->channel_key); + if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key)) + return; if (key_len) len = key_len; else if (channel->key_len) len = channel->key_len / 8; else - len = sizeof(channel_key); + len = silc_cipher_get_key_len(channel->channel_key) / 8; /* Create channel key */ for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng); @@ -2735,3 +2819,23 @@ void silc_server_announce_channels(SilcServer server) silc_buffer_free(channel_users); } } + +/* Failure timeout callback. If this is called then we will immediately + process the received failure. We always process the failure with timeout + since we do not want to blindly trust to received failure packets. + This won't be called (the timeout is cancelled) if the failure was + bogus (it is bogus if remote does not close the connection after sending + the failure). */ + +SILC_TASK_CALLBACK(silc_server_failure_callback) +{ + SilcServerFailureContext f = (SilcServerFailureContext)context; + + if (f->sock->protocol) { + f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE; + f->sock->protocol->execute(f->server->timeout_queue, 0, + f->sock->protocol, f->sock->sock, 0, 0); + } + + silc_free(f); +} diff --git a/apps/silcd/server.h b/apps/silcd/server.h index 2a0e30df..e8b31ca7 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -120,11 +120,13 @@ void silc_server_disconnect_remote(SilcServer server, SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcServerID *router_id, char *cipher, + char *hmac, char *channel_name, int broadcast); SilcChannelEntry silc_server_create_new_channel_with_id(SilcServer server, char *cipher, + char *hmac, char *channel_name, SilcChannelID *channel_id, int broadcast); diff --git a/apps/silcd/server_internal.h b/apps/silcd/server_internal.h index bc0e3d75..0d059f4f 100644 --- a/apps/silcd/server_internal.h +++ b/apps/silcd/server_internal.h @@ -151,6 +151,15 @@ typedef struct { SilcServer server; } *SilcServerHBContext; +/* Failure context. This is allocated when failure packet is received. + Failure packets are processed with timeout and data is saved in this + structure. */ +typedef struct { + SilcServer server; + SilcSocketConnection sock; + unsigned int failure; +} *SilcServerFailureContext; + /* Macros */ /* Registers generic task for file descriptor for reading from network and diff --git a/apps/silcd/serverconfig.c b/apps/silcd/serverconfig.c index 35e23418..485c261a 100644 --- a/apps/silcd/serverconfig.c +++ b/apps/silcd/serverconfig.c @@ -22,148 +22,15 @@ #include "serverincludes.h" #include "server_internal.h" -/* XXX - All possible configuration sections for SILC server. - - - - Format: - - +: - - - - Format: - - +: - - - - Format: - - +: - - - - This section is used to set the server informations. - - Format: - - +::: - - - - This section is used to set the server's administrative information. - - Format: - - +::: - - - - This section is used to set ports the server is listenning. - - Format: - - +:: - - - - This section is used to set both the user and group which silcd - sets itself upon starting. - - Format: - - : - - - - This section is used to set various logging files, their paths - and maximum sizes. All the other directives except those defined - below are ignored in this section. Log files are purged after they - reach the maximum set byte size. - - Format: - - +infologfile:: - +errorlogfile:: - - - - This section is used to define connection classes. These can be - used to optimize the server and the connections. - - Format: - - +::: - - - - This section is used to define client authentications. - - Format: - - +:::: - - - - This section is used to define the server's administration - authentications. - - Format: - - +:::: - - - - This section is used to define the server connections to this - server/router. Only routers can have normal server connections. - Normal servers leave this section epmty. The remote server cannot be - older than specified Version ID. - - Format: - - +::::: - - - - This section is used to define the router connections to this - server/router. Both normal server and router can have router - connections. Normal server usually has only one connection while - a router can have multiple. The remote server cannot be older than - specified Version ID. - - Format: - - +::: - ::: - - - - This section is used to deny specific connections to your server. This - can be used to deny both clients and servers. - - Format: - - +: