updates.
authorPekka Riikonen <priikone@silcnet.org>
Fri, 9 Mar 2001 00:22:35 +0000 (00:22 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Fri, 9 Mar 2001 00:22:35 +0000 (00:22 +0000)
68 files changed:
CHANGES
TODO
apps/silc/client_ops.c
apps/silc/client_ops.h
apps/silc/clientconfig.c
apps/silc/clientconfig.h
apps/silc/local_command.c
apps/silc/silc.c
apps/silc/testi.conf
apps/silc/testi2.conf
apps/silcd/command.c
apps/silcd/command_reply.c
apps/silcd/idlist.c
apps/silcd/idlist.h
apps/silcd/packet_receive.c
apps/silcd/packet_receive.h
apps/silcd/packet_send.c
apps/silcd/packet_send.h
apps/silcd/protocol.c
apps/silcd/protocol.h
apps/silcd/server.c
apps/silcd/server.h
apps/silcd/server_internal.h
apps/silcd/serverconfig.c
apps/silcd/serverconfig.h
apps/silcd/testi2.conf
doc/draft-riikonen-silc-ke-auth-01.nroff
doc/draft-riikonen-silc-pp-01.nroff
doc/draft-riikonen-silc-spec-01.nroff
doc/example_silc.conf
doc/example_silcd.conf
lib/silcclient/Makefile.am
lib/silcclient/README
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_channel.c [new file with mode: 0644]
lib/silcclient/client_internal.h [new file with mode: 0644]
lib/silcclient/client_keyagr.c [new file with mode: 0644]
lib/silcclient/client_notify.c [new file with mode: 0644]
lib/silcclient/client_prvmsg.c [new file with mode: 0644]
lib/silcclient/command.c
lib/silcclient/command_reply.c
lib/silcclient/idlist.c
lib/silcclient/idlist.h
lib/silcclient/protocol.c
lib/silcclient/protocol.h
lib/silcclient/silcapi.h
lib/silccore/silcauth.c
lib/silccore/silcchannel.c
lib/silccore/silcchannel.h
lib/silccore/silcmode.h
lib/silccore/silcpacket.c
lib/silccore/silcprotocol.c
lib/silccrypt/silccipher.c
lib/silccrypt/silccipher.h
lib/silccrypt/silchash.c
lib/silccrypt/silchash.h
lib/silccrypt/silchmac.c
lib/silccrypt/silchmac.h
lib/silccrypt/silcpkcs.c
lib/silcske/payload.c
lib/silcske/payload_internal.h
lib/silcske/silcske.c
lib/silcske/silcske.h
lib/silcske/silcske_status.h
lib/silcutil/silcbuffmt.c
lib/silcutil/silctask.c
lib/silcutil/silctask.h

diff --git a/CHANGES b/CHANGES
index 9a644b764b53943af2faa301a78046c36b22b50e..47f3c148a4db95c6c1df3682ec353ac7120a527d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,166 @@
+Thu Mar  8 21:39:03 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * 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 [<key len>] option from CMODE command as the cipher
+         name decides the key length, nowadays.  See the defined ciphers
+         from the protocol specification.
+
+       * Added [<hmac>] option to the CMODE command to define the HMAC
+         for the channel.  Added SILC_CMODE_HMAC channel mode.
+
+       * Added [<hmac>] 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 <priikone@poseidon.pspt.fi>
+
+       * 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 <priikone@poseidon.pspt.fi>
+
+       * 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 <priikone@poseidon.pspt.fi>
+
+       * 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 <priikone@poseidon.pspt.fi>
 
        * Some "Incomplete WHOIS info" errors has been appearing on the
diff --git a/TODO b/TODO
index c72f3504899fddfe5c5fba19d7b2ddc9902cad3d..c408e032b7919882300ba914753396e9ebe70f74 100644 (file)
--- 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
index 48ba11526bd53325064bbad1f508b91376b58588..acf3186a237fbc8fdda1d116d03c22a61777b4da 100644 (file)
@@ -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,
 };
index 7d58937f0794c750039d085be1f054c2ca10ab71..2dc68ce1d4a30388d56b65d574a8836da2efc04a 100644 (file)
@@ -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
index 65d9b2756fb4b714f7d86c240fa0321fcc67a5dc..ed7e4b7a0f72f70e124995af04de1a53f348bc3e 100644 (file)
@@ -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)
index f2c7a4afa97119a6b846ed12bf22c990eba32a3f..30221a7ac5a09a42dc4b3a9261b6cc676613b5c6 100644 (file)
@@ -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);
index 6429bfc5a10bb5854046e8a6d45ff8ea62efd041..065da9385f4d58df097ff8680d74eff50df2cdef 100644 (file)
@@ -20,6 +20,7 @@
 /* $Id$ */
 
 #include "clientincludes.h"
+#include "client_internal.h"
 
 /* Local commands. */
 SilcClientCommand silc_local_command_list[] =
index 3ebe26a080449f33336c3349d41579ac3d522dac..bfb4ab72b9fad180a9145e34c22153c999cc2cec 100644 (file)
@@ -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)
index 1289ea7c05424b72009b53a34c656f8586a1539c..2976768edd0d92ffee1339c3c3b33a904a127c62 100644 (file)
@@ -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: <name>:<hash name>:<mac length>
+#
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
+
 #
 # Configured PKCS.
 #
index 64f3839f1ef6705a11802410e314f8f158a77c6a..b6935b7f4e20fb1113ce13929aee532ebd121ce9 100644 (file)
@@ -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
index 78b90087bf315b9f121f941455043e1decdad123..779c0385f6cee58c88eccd6ac2083a4defdbf02d 100644 (file)
@@ -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);
index 7086cc17a4b87064ef0658177882f9ba967d80f7..0034de113b2c9ac442892a78fae7802cb9431e39 100644 (file)
@@ -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;
index d8a747fcacd85cd140b87935f223aa62fa1c1027..b40ff6a6b1ed3826eaeefb27144a146d692f8951 100644 (file)
@@ -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);
 
index 444f9108cc2e01e8c928837d57ee0321f6b0bb5a..bbce9ea529ef391d9e34a8a9efcacf07fdccca34 100644 (file)
@@ -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,
index ef57bcd6acf250e4df931a5c280b72c71979815a..403c5928493d82f0e81757297f43537c7ba66af1 100644 (file)
@@ -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;
+    }
+  }
+}
index 15d6b22f7813ae0cac0041aaa4bd303a9bbef5fb..cc450d77e0fe50c1712d4c3534960e720d529291 100644 (file)
@@ -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
index 840fd68b3aa2d8dc3ed863c5896107a708782a14..282289870851f003af97613b35d449288e184c40 100644 (file)
@@ -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);
+}
index 87b072f66a7b46c39fe21fb448f6cb8ee6826a4a..89daefe2d3a74126292d747fbbbed8057be53728 100644 (file)
@@ -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
index ad86e001293603e345f9931b3bab9928a28dc100..ca091612ba9dd8d1a9170284c29fe09940ffaba7 100644 (file)
@@ -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
index d6f54663d5928a409c713eddf73d3c4e0ad50f28..7b57da2d67448d0dade022515de06478803d3f9c 100644 (file)
@@ -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
index 5db18ef609a3c1a655e04ea88b0714b93e681728..765f6be552630cffa935a80a39e1c75e33439625 100644 (file)
@@ -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);
+}
index 2a0e30df62aab4e52ee5a30dc157dac7bc2c0c59..e8b31ca7857814ac9e20dde7f1dd7559d451cddd 100644 (file)
@@ -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);
index bc0e3d755d8bacb00cdb1bc43e946bc70e84471f..0d059f4f81dccf2fe7ea9ef1ea0c2e21ec410da4 100644 (file)
@@ -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
index 35e234183a09558a4979f329ef7b937368c6bc16..485c261a7ae5f6789862f11ed636c980bfa0871b 100644 (file)
 #include "serverincludes.h"
 #include "server_internal.h"
 
-/*  XXX
-   All possible configuration sections for SILC server. 
-
-   <Cipher>
-
-       Format:
-
-       +<Cipher name>:<SIM path>
-
-   <PKCS>
-
-       Format:
-
-       +<PKCS name>:<key length>
-
-   <HashFunction>
-
-       Format:
-
-       +<Hash function name>:<SIM path>
-
-   <ServerInfo>
-
-       This section is used to set the server informations.
-
-       Format:
-
-       +<Server DNS name>:<Server IP>:<Geographic location>:<Port>
-
-   <AdminInfo>
-
-       This section is used to set the server's administrative information.
-
-       Format:
-
-       +<Location>:<Server type>:<Admin's name>:<Admin's email address>
-
-   <ListenPort>
-
-       This section is used to set ports the server is listenning.
-
-       Format:
-
-       +<Local IP/UNIX socket path>:<Remote IP>:<Port>
-
-   <Identity>
-
-       This section is used to set both the user and group which silcd
-       sets itself upon starting.
-
-       Format:
-
-       <user>:<group>
-
-   <Logging>
-
-       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:<path>:<max byte size>
-       +errorlogfile:<path>:<max byte size>
-
-   <ConnectionClass>
-
-       This section is used to define connection classes. These can be
-       used to optimize the server and the connections.
-
-       Format:
-
-       +<Class number>:<Ping freq>:<Connect freq>:<Max links>
-
-   <ClientAuth>
-
-       This section is used to define client authentications.
-
-       Format:
-
-       +<Remote address or name>:<auth method>:<password/cert/key/???>:<Port>:<Class>
-
-   <AdminAuth>
-
-       This section is used to define the server's administration 
-       authentications.
-
-       Format:
-
-       +<Hostname>:<auth method>:<password/cert/key/???>:<Nickname hash>:<Class>
-
-   <ServerConnection>
-
-       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:
-
-       +<Remote address or name>:<auth method>:<password/key/???>:<Port>:<Version ID>:<Class>
-
-   <RouterConnection>
-
-       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:
-
-       +<Remote address or name>:<auth method>:<password/key/???>:
-       <Port>:<Version ID>:<Class>:<Initiator>
-
-   <DenyConnection>
-
-       This section is used to deny specific connections to your server. This
-       can be used to deny both clients and servers.
-
-       Format:
-
-       +<Remote address or name or nickname>:<Time interval>:<Comment>:<Port>
-
-   <RedirectClient>
-
-       This section is used to set the alternate servers that clients will be
-       redirected to when our server is full.
-
-       Format:
-
-       +<Remote address or name>:<Port>
-
-*/
 SilcServerConfigSection silc_server_config_sections[] = {
   { "[Cipher]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
   { "[PKCS]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 2 },
-  { "[HashFunction]", 
+  { "[Hash]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
+  { "[hmac]", 
+    SILC_CONFIG_SERVER_SECTION_TYPE_HMAC, 3 },
   { "[ServerInfo]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO, 4 },
   { "[AdminInfo]", 
@@ -536,6 +403,46 @@ int silc_server_config_parse_lines(SilcServerConfig config,
       checkmask |= (1L << pc->section->type);
       break;
 
+    case SILC_CONFIG_SERVER_SECTION_TYPE_HMAC:
+
+      SILC_SERVER_CONFIG_LIST_ALLOC(config->hmac);
+
+      /* 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 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;
+      checkmask |= (1L << pc->section->type);
+      break;
+
     case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:
 
       if (!config->server_info)
@@ -1063,6 +970,8 @@ int silc_server_config_parse_lines(SilcServerConfig 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->listen_port && config->listen_port->prev)
     config->listen_port = config->listen_port->prev;
   while (config->logging && config->logging->prev)
@@ -1298,7 +1207,7 @@ void silc_server_config_register_hashfuncs(SilcServerConfig config)
        silc_server_stop(server);
        exit(1);
       }
-      silc_free(tmp);
+      silc_hash_free(tmp);
 
 #ifdef SILC_SIM
     } else {
@@ -1341,7 +1250,7 @@ void silc_server_config_register_hashfuncs(SilcServerConfig config)
        exit(1);
       }
 
-      /* Register the cipher */
+      /* Register the hash function */
       silc_hash_register(&hash);
 #endif
     }
@@ -1350,6 +1259,43 @@ void silc_server_config_register_hashfuncs(SilcServerConfig config)
   }
 }
 
+/* Registers configure HMACs. These can then be allocated by the server
+   when needed. */
+
+void silc_server_config_register_hmacs(SilcServerConfig config)
+{
+  SilcServerConfigSectionAlg *alg;
+  SilcServer server = (SilcServer)config->server;
+
+  SILC_LOG_DEBUG(("Registering configured HMACs"));
+
+  if (!config->hmac) {
+    SILC_LOG_ERROR(("HMACs are not configured. SILC cannot work without "
+                   "HMACs"));
+    silc_server_stop(server);
+    exit(1);
+  }
+
+  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_server_stop(server);
+      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;
+  }
+}
+
 /* Returns client authentication information from server configuration
    by host (name or ip). */
 
index ef26de850b7ee22fcffa9180458a5a236d3499fb..1d33597d8528ab6ea5829be365a9af10d3f8a984 100644 (file)
@@ -164,6 +164,7 @@ typedef struct {
   SilcServerConfigSectionAlg *cipher;
   SilcServerConfigSectionAlg *pkcs;
   SilcServerConfigSectionAlg *hash_func;
+  SilcServerConfigSectionAlg *hmac;
   SilcServerConfigSectionServerInfo *server_info;
   SilcServerConfigSectionAdminInfo *admin_info;
   SilcServerConfigSectionListenPort *listen_port;
@@ -187,6 +188,7 @@ typedef enum {
   SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER,
   SILC_CONFIG_SERVER_SECTION_TYPE_PKCS,
   SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION,
+  SILC_CONFIG_SERVER_SECTION_TYPE_HMAC,
   SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO,
   SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO,
   SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT,
@@ -254,6 +256,7 @@ void silc_server_config_setlogfiles(SilcServerConfig config);
 void silc_server_config_register_ciphers(SilcServerConfig config);
 void silc_server_config_register_pkcs(SilcServerConfig config);
 void silc_server_config_register_hashfuncs(SilcServerConfig config);
+void silc_server_config_register_hmacs(SilcServerConfig config);
 SilcServerConfigSectionClientConnection *
 silc_server_config_find_client_conn(SilcServerConfig config, 
                                    char *host, int port);
index 50064d06d312213d69701d4b9a19f14103d3e2f4..3f42e74f2733489f65b9be28c285718c110b1f1f 100644 (file)
@@ -10,10 +10,16 @@ mars-192-cbc:../lib/silcsim/modules/mars.sim.so:24:16
 mars-128-cbc:../lib/silcsim/modules/mars.sim.so:16:16
 none:../lib/silcsim/modules/none.sim.so:0:0
 
-[HashFunction]
+[Hash]
 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
@@ -25,10 +31,10 @@ nobody:nobody
 Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
 
 [ServerInfo]
-lassi.kuo.fi.ssh.com:212.146.42.253:Kuopio, Finland:1334
+lassi.kuo.fi.ssh.com:212.146.42.253:Kuopio, Finland:1335
 
 [ListenPort]
-212.146.42.253:212.146.42.253:1334
+212.146.42.253:212.146.42.253:1335
 
 [Logging]
 infologfile:silcd2.log:10000
@@ -49,10 +55,10 @@ errorlogfile:silcd2.log:10000
 [AdminConnection]
 
 [ServerConnection]
-212.146.42.253:passwd:priikone:1333:1:1
+212.146.42.253:passwd:priikone:1336:1:1
 
 [RouterConnection]
-212.146.42.253:passwd:priikone:1335:1:1:0
+212.146.42.253:passwd:priikone:1334:1:1:0
 
 [DenyConnection]
 [RedirectClient]
index e7cee0081d9b42076afefe29e8fc99f33f19a999..87030a7cbeda71c7c35e59a256aa5d73a26a3c57 100644 (file)
@@ -263,7 +263,13 @@ not include spaces (` ').
 |       Hash Alg Length         |                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
 |                                                               |
-~                        Hash Algorithms                        ~
+~                         Hash Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         HMAC Length           |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                             HMACs                             ~
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |    Compression Alg Length     |                               |
@@ -342,10 +348,19 @@ o Encryption Alg Length (2 bytes) - The length of the encryption
 o Encryption Algorithms (variable length) - The list of
   encryption algorithms.
 
-o Hash Alg Length (2 bytes) - The length of the Hash algorithms
+o Hash Alg Length (2 bytes) - The length of the Hash algorithm
   list, not including any other field.
 
-o Hash Algorithms (variable length) - The list of Hash algorithms.
+o Hash Algorithms (variable length) - The list of Hash
+  algorithms.  The hash algorithms are mainly used in the
+  SKE protocol.
+
+o HMAC Length (2 bytes) - The length of the HMAC list, not
+  including any other field.
+
+o HMACs (variable length) - The list of HMACs.  The HMAC's
+  are used to compute the Message Authentication Codes (MAC)
+  of the SILC packets.
 
 o Compression Alg Length (2 bytes) - The length of the
   compression algorithms list, not including any other field.
@@ -431,7 +446,8 @@ o Public Key Type (2 bytes) - The public key (or certificate)
   certificate specification in [PGP].  See SPKI certificate
   specification in [SPKI].  If this field includes zero (0)
   or unsupported type number the protocol must be aborted
-  sending SILC_PACKET_FAILURE message.
+  sending SILC_PACKET_FAILURE message and the connection should
+  be closed immediately.
 
 o Public Data Length (2 bytes) - The length of the public
   data computed by the responder, not including any other
@@ -574,10 +590,10 @@ Setup:  p is a large and public safe prime.  This is one of the
 
 
 If any of these phases is to fail SILC_PACKET_FAILURE is sent to
-indicate that the key exchange protocol failed.  Any other packets must
-not be sent or accepted during the key exchange except the
-SILC_PACKET_KEY_EXCHANGE_*, SILC_PACKET_DISCONNECT, SILC_PACKET_FAILURE
-and/or SILC_PACKET_SUCCESS packets.
+indicate that the key exchange protocol has failed, and the connection
+should be closed immediately.  Any other packets must not be sent or
+accepted during the key exchange except the SILC_PACKET_KEY_EXCHANGE_*,
+SILC_PACKET_FAILURE and SILC_PACKET_SUCCESS packets.
 
 The result of this protocol is a shared secret key material KEY and
 a hash value HASH.  The key material itself is not fit to be used as 
@@ -791,17 +807,22 @@ defined:
     None of the provided hash functions were supported.
 
 
-7   SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
+7   SILC_SKE_STATUS_UNSUPPORTED_HMAC
+
+    None of the provided HMACs were supported.
+
+
+8   SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
 
     Provided public key type is not supported.
 
 
-8   SILC_SKE_STATUS_INCORRECT_SIGNATURE
+9   SILC_SKE_STATUS_INCORRECT_SIGNATURE
 
     Provided signature was incorrect.
 
 
-9   SILC_SKE_STATUS_BAD_VERSION
+10   SILC_SKE_STATUS_BAD_VERSION
 
     Provided version string was not acceptable.
 .in 3
index 3edb41106d7206e615f74865cb849145aff9ada1..a8d9194720751c97d39e538ef98a712c081e0d4e 100644 (file)
@@ -1308,6 +1308,10 @@ represents the Channel Message Payload.
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                                                               |
+~                              MAC                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
 ~                       Initial Vector *                        ~
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -1333,6 +1337,16 @@ o Padding (variable length) - The padding that must be
   applied because this payload is encrypted separately from
   other parts of the packet.
 
+o MAC (variable legnth) - The MAC computed from the
+  Message Length, Message Data, Padding Length and Padding
+  fields.  This protects the integrity of the plaintext
+  channel message.  The receiver can verify from the MAC
+  whether the message decrypted correctly.  Also, if more than
+  one private key has been set for the channel, the receiver
+  can verify which of the keys decrypted the message 
+  correctly.  Note that, this field is encrypted and must
+  be added to the padding calculation.
+
 o Initial Vector (variable length) - The initial vector
   that has been used in packet encryption.  It needs to be
   used in the packet decryption as well.  What this field
@@ -2080,7 +2094,7 @@ As the receiver founds the packet to be channel message, rest of the
 packet processing is special.  Rest of the SILC Packet header is
 decrypted with the same session key along with the padding of the
 packet.  After that the packet is protected with the channel specific
-key and hence can be decrypted only if the receiver is the client on
+key and thus can be decrypted only if the receiver is the client on
 the channel.  See section 2.7 Packet Padding Generation for more
 information about padding on special packets.
 
index 473c8fe458c89e47087ac5af3bc87d95b32cfcd9..d8163389913a89502629b07d959c44c1e5c5a128 100644 (file)
@@ -104,8 +104,9 @@ Table of Contents
   3.10 Algorithms ............................................... 20
       3.10.1 Ciphers ............................................ 20
       3.10.2 Public Key Algorithms .............................. 21
-      3.10.3 MAC Algorithms ..................................... 21
-      3.10.4 Compression Algorithms ............................. 22
+      3.10.3 Hash Functions ..................................... XXX
+      3.10.4 MAC Algorithms ..................................... XXX
+      3.10.5 Compression Algorithms ............................. XXX
   3.11 SILC Public Key .......................................... 22
   3.12 SILC Version Detection ................................... 24
 4 SILC Procedures ............................................... 25
@@ -1212,8 +1213,21 @@ is always in PKCS #1 version 1.5 format.
 Additional public key algorithms may be defined to be used in SILC.
 
 
+.ti0
+3.10.3 Hash Functions
+
+Hash functions are used as part of MAC algorithms defined in the next
+section.  They are also used in the SILC Key Exchange protocol defined
+in the [SILC3].
+
+Following Hash algorithm are defined in SILC protocol:
+
+sha1             SHA-1, length = 20      (mandatory)
+md5              MD5, length = 16        (optional)
+
+
 .ti 0
-3.10.3 MAC Algorithms
+3.10.4 MAC Algorithms
 
 Data integrity is protected by computing a message authentication code
 (MAC) of the packet data.  See [SILC2] for details how to compute the
@@ -1222,7 +1236,9 @@ MAC.
 Following MAC algorithms are defined in SILC protocol:
 
 .in 6
-hmac-sha1        HMAC-SHA1, length = 20  (mandatory)
+hmac-sha1-96     HMAC-SHA1, length = 12  (mandatory)
+hmac-md5-96      HMAC-MD5, length = 12   (optional)
+hmac-sha1        HMAC-SHA1, length = 20  (optional)
 hmac-md5         HMAC-MD5, length = 16   (optional)
 none             No MAC                  (optional)
 .in 3
@@ -1240,7 +1256,7 @@ Additional MAC algorithms may be defined to be used in SILC.
 
 
 .ti 0
-3.10.4 Compression Algorithms
+3.10.5 Compression Algorithms
 
 SILC protocol supports compression that may be applied to unencrypted
 data.  It is recommended to use compression on slow links as it may
@@ -1602,6 +1618,17 @@ send the new key to its router.  See [SILC2] on more information about
 how channel messages must be encrypted and decrypted when router is
 processing them.
 
+When client receives the SILC_PACKET_CHANNEL_KEY packet with the
+Channel Key Payload it must process the key data to create encryption
+and decryption key, and to create the HMAC key that is used to compute
+the MACs of the channel messages.  The processing is as follows:
+
+  channel_key  = raw key data
+  HMAC key     = hash(raw key data)
+
+The raw key data is the key data received in the Channel Key Payload.
+The hash() function is the hash function used in the HMAC of the channel.
+
 
 .ti 0
 4.5 Private Message Sending and Reception
@@ -2361,9 +2388,10 @@ List of all defined commands in SILC follows.
 
    14   SILC_COMMAND_JOIN
 
-        Max Arguments:  4
+        Max Arguments:  5
             Arguments:  (1) <channel>       (2) <Client ID>
                         (3) [<passphrase>]  (4) [<cipher>]
+                        (5) [<hmac>]
 
         Join to channel/create new channel.  This command is used to
         join to a channel.  If the channel does not exist the channel is
@@ -2383,7 +2411,9 @@ List of all defined commands in SILC follows.
         requested by sending the name of the requested <cipher>.  This
         is used only if the channel does not exist and is created.  If
         the channel already exists the cipher set previously for the
-        channel will be used to secure the traffic.
+        channel will be used to secure the traffic.  The computed MACs
+        of the channel message are produced by the default HMAC or by
+        the <hmac> provided for the command.
 
         The server must check whether the user is allowed to join to
         the requested channel.  Various modes set to the channel affect
@@ -2403,12 +2433,12 @@ List of all defined commands in SILC follows.
 
         Reply messages to the command:
 
-        Max Arguments:  9
+        Max Arguments:  10
             Arguments:  (1) <Status Payload>  (2) <channel> 
                         (3) <Channel ID>      (4) <channel mode mask>
                         (5) <created>         (6) <Channel Key Payload>
                         (7) [<ban mask>]      (8) [<invite list>]
-                        (9) [<topic>]
+                        (9) [<topic>]        (10) [<hmac>]
 
         This command replies with the channel name requested by the
         client, channel ID of the channel and topic of the channel
@@ -2522,11 +2552,11 @@ List of all defined commands in SILC follows.
 
    17   SILC_COMMAND_CMODE
 
-        Max Arguments:  7
+        Max Arguments:  8
             Arguments:  (1) <Channel ID>    (2) <channel mode mask>
                         (3) [<user limit>]  (4) [<passphrase>]
                         (5) [<ban mask>]    (6) [<invite list>]
-                        (7) [<cipher>[:<key len>]]
+                        (7) [<cipher>]      (8) [<hmac>]
 
         This command is used by client to set or change channel flags on
         a channel.  Channel has several modes that set various properties
@@ -2698,9 +2728,7 @@ List of all defined commands in SILC follows.
               Sets specific cipher to be used to protect channel
               traffic.  The <cipher> argument is the requested cipher.
               When set or unset the server must re-generate new
-              channel key.  If <key len> argument is specified with
-              <cipher> argument the new key is generated of <key len>
-              length in bits.  Only channel founder may set the cipher of 
+              channel key.  Only channel founder may set the cipher of 
               the channel.  When unset the new key is generated using
               default cipher for the channel.
 
@@ -2708,6 +2736,16 @@ List of all defined commands in SILC follows.
               to set/unset this mode.
 
 
+           0x0400    SILC_CMODE_HMAC
+
+              Sets specific hmac to be used to compute the MACs of the
+              channel message.  The <hmac> argument is the requested hmac.
+              Only channel founder may set the hmac of the channel.
+
+              Typical implementation would use [+|-]h on user interface
+              to set/unset this mode.
+
+
         To make the mode system work, client must keep the channel mode
         mask locally so that the mode setting and unsetting would work
         without problems.  The client receives the initial channel mode
index 5949ca2a1a91ec70ef1a28e76adf53cdce79e746..6b88d8125497d225c7df4825bfe1053bdb92dd85 100644 (file)
@@ -25,8 +25,20 @@ none:../lib/silcsim/modules/none.sim.so:0:0
 # If the hash function is builtin the <module path> maybe omitted.
 #
 [hash]
-md5::64:16
 sha1::64:20
+md5::64:16
+
+#
+# Configured HMAC functions. The hash function used in the HMAC must
+# configured to the [hash] section.
+#
+# Format: <name>:<hash name>:<mac length>
+#
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
 
 #
 # Configured PKCS.
index 236e200ef44946966f8756f7df1beb5619125db1..8d4bcf59bba644b9e6b0b4a6fa35a28fb412d578 100644 (file)
@@ -24,9 +24,21 @@ none:../lib/silcsim/modules/none.sim.so:0:0
 #
 # If the hash function is builtin the <module path> maybe omitted.
 #
-[HashFunction]
-md5::64:16
+[Hash]
 sha1::64:20
+md5::64:16
+
+#
+# Configured HMAC functions. The hash function used in the HMAC must
+# configured to the [hash] section.
+#
+# Format: <name>:<hash name>:<mac length>
+#
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
 
 #
 # Configured PKCS.
index 07df38868eb88168e429badd02bd3316bdf437e9..ba49dbae83cceccc49d0b2e68f383cea13a43d3a 100644 (file)
@@ -22,6 +22,10 @@ noinst_LIBRARIES = libsilcclient.a
 
 libsilcclient_a_SOURCES = \
        client.c \
+       client_keyagr.c \
+       client_notify.c \
+       client_prvmsg.c \
+       client_channel.c \
        command.c \
        command_reply.c \
        idlist.c \
index 39555b6d4ce7b25aea514d2e683c3119dc890b99..85748c280e6cd16a68da9692b2cdcbb65eb982f9 100644 (file)
@@ -8,7 +8,7 @@ designed to be complete SILC Client implementation without actual user
 interface.  The library provides the API for the application which it can
 use to implement generally whatever user interface it wants.
 
-The `ops.h' file defines the function prototypes that application must
+The `silcapi.h' file defines the function prototypes that application must
 implement in order to be able to create the user interface with the
 library.  The idea is that the application can implement whatever user
 interface routines in the functions and display the data whatever way
index 32e0a32dadde308b06e00a80c93797ec68aff503..5b68a7715232c3399075bd1d5c7f8fffff855352 100644 (file)
 /* $Id$ */
 
 #include "clientlibincludes.h"
+#include "client_internal.h"
 
 /* Static task callback prototypes */
 SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
 SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
 SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
-SILC_TASK_CALLBACK(silc_client_packet_process);
 SILC_TASK_CALLBACK(silc_client_packet_parse_real);
 
 static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
@@ -179,18 +179,6 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
     }
 }
 
-/* Internal context for connection process. This is needed as we
-   doing asynchronous connecting. */
-typedef struct {
-  SilcClient client;
-  SilcClientConnection conn;
-  SilcTask task;
-  int sock;
-  char *host;
-  int port;
-  int tries;
-} SilcClientInternalConnectContext;
-
 static int 
 silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
 {
@@ -274,11 +262,6 @@ int silc_client_start_key_exchange(SilcClient client,
 
   /* Allocate new socket connection object */
   silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
-  if (conn->sock == NULL) {
-    client->ops->say(client, conn, 
-                    "Error: Could not allocate connection socket");
-    return FALSE;
-  }
 
   conn->nickname = strdup(client->username);
   conn->sock->hostname = conn->remote_host;
@@ -292,6 +275,7 @@ int silc_client_start_key_exchange(SilcClient client,
   proto_ctx->sock = conn->sock;
   proto_ctx->rng = client->rng;
   proto_ctx->responder = FALSE;
+  proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
 
   /* Perform key exchange protocol. silc_client_connect_to_server_final
      will be called after the protocol is finished. */
@@ -396,11 +380,14 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
     /* Error occured during protocol */
     SILC_LOG_DEBUG(("Error during KE protocol"));
     silc_protocol_free(protocol);
+    silc_ske_free_key_material(ctx->keymat);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     ctx->sock->protocol = NULL;
+    silc_task_unregister_by_callback(client->timeout_queue,
+                                    silc_client_failure_callback);
 
     /* Notify application of failure */
     client->ops->connect(client, ctx->sock->user_data, FALSE);
@@ -408,6 +395,16 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
     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. */
+  silc_client_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);
+  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));
@@ -475,6 +472,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     conn->sock->protocol = NULL;
+    silc_task_unregister_by_callback(client->timeout_queue,
+                                    silc_client_failure_callback);
 
     /* Notify application of failure */
     client->ops->connect(client, ctx->sock->user_data, FALSE);
@@ -508,6 +507,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
   conn->remote_id_data_len = SILC_ID_SERVER_LEN;
 
+  silc_task_unregister_by_callback(client->timeout_queue,
+                                  silc_client_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->auth_data)
     silc_free(ctx->auth_data);
@@ -521,9 +522,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
    is used directly only in special cases. Normal cases should use
    silc_server_packet_send. Returns < 0 on error. */
 
-static int silc_client_packet_send_real(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       int force_send)
+int silc_client_packet_send_real(SilcClient client,
+                                SilcSocketConnection sock,
+                                int force_send)
 {
   int ret;
 
@@ -549,7 +550,7 @@ static int silc_client_packet_send_real(SilcClient client,
 /* Packet processing callback. This is used to send and receive packets
    from network. This is generic task. */
 
-SILC_TASK_CALLBACK(silc_client_packet_process)
+SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 {
   SilcClient client = (SilcClient)context;
   SilcSocketConnection sock = NULL;
@@ -713,23 +714,7 @@ void silc_client_packet_parse_type(SilcClient client,
      * error and call the protocol callback. This fill cause error on
      * protocol and it will call the final callback.
      */
-    if (sock->protocol) {
-      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-
-      /* XXX We have only two protocols currently thus we know what this
-        failure indication is. */
-      if (buffer->len >= 4) {
-       unsigned int failure;
-
-       SILC_GET32_MSB(failure, buffer->data);
-
-       /* Notify application */
-       client->ops->failure(client, sock->user_data, sock->protocol,
-                            (void *)failure);
-      }
-    }
+    silc_client_process_failure(client, sock, packet);
     break;
   case SILC_PACKET_REJECT:
     break;
@@ -865,6 +850,14 @@ void silc_client_packet_parse_type(SilcClient client,
     SILC_LOG_DEBUG(("Heartbeat packet"));
     break;
 
+  case SILC_PACKET_KEY_AGREEMENT:
+    /*
+     * Received key agreement packet
+     */
+    SILC_LOG_DEBUG(("Key agreement packet"));
+    silc_client_key_agreement(client, sock, packet);
+    break;
+
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -958,220 +951,6 @@ void silc_client_packet_send(SilcClient client,
   silc_client_packet_send_real(client, sock, force_send);
 }
 
-/* Sends packet to the `channel'. Packet to channel is always encrypted
-   differently from "normal" packets. SILC header of the packet is 
-   encrypted with the next receiver's key and the rest of the packet is
-   encrypted with the channel specific key. Padding and HMAC is computed
-   with the next receiver's key. The `data' is the channel message. If
-   the `force_send' is TRUE then the packet is sent immediately. */
-
-void silc_client_send_channel_message(SilcClient client, 
-                                     SilcClientConnection conn,
-                                     SilcChannelEntry channel,
-                                     unsigned char *data, 
-                                     unsigned int data_len, 
-                                     int force_send)
-{
-  int i;
-  SilcSocketConnection sock = conn->sock;
-  SilcBuffer payload;
-  SilcPacketContext packetdata;
-  SilcCipher cipher;
-  SilcHmac hmac;
-  unsigned char *id_string;
-  unsigned int block_len;
-
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
-  if (!channel || !channel->key) {
-    client->ops->say(client, conn, 
-                    "Cannot talk to channel: key does not exist");
-    return;
-  }
-
-  /* Generate IV */
-  block_len = silc_cipher_get_block_len(channel->channel_key);
-  if (channel->iv[0] == '\0')
-    for (i = 0; i < block_len; i++) channel->iv[i] = 
-                                     silc_rng_get_byte(client->rng);
-  else
-    silc_hash_make(client->md5hash, channel->iv, block_len, channel->iv);
-
-  /* Encode the channel payload */
-  payload = silc_channel_payload_encode(data_len, data, block_len, 
-                                       channel->iv, client->rng);
-  if (!payload) {
-    client->ops->say(client, conn, 
-                    "Error: Could not create packet to be sent to channel");
-    return;
-  }
-
-  /* Get data used in packet header encryption, keys and stuff. Rest
-     of the packet (the payload) is, however, encrypted with the 
-     specified channel key. */
-  cipher = conn->send_key;
-  hmac = conn->hmac;
-  id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-
-  /* Set the packet context pointers. The destination ID is always
-     the Channel ID of the channel. Server and router will handle the
-     distribution of the packet. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  packetdata.dst_id = id_string;
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          payload->len);
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Encrypt payload of the packet. This is encrypted with the channel key. */
-  channel->channel_key->cipher->encrypt(channel->channel_key->context,
-                                       payload->data, payload->data,
-                                       payload->len - block_len, /* -IV_LEN */
-                                       channel->iv);
-
-  /* Put the actual encrypted payload data into the buffer. */
-  silc_buffer_put(sock->outbuf, payload->data, payload->len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Encrypt the header and padding of the packet. This is encrypted 
-     with normal session key shared with our server. */
-  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-  silc_buffer_free(payload);
-  silc_free(id_string);
-}
-
-/* Sends private message to remote client. If private message key has
-   not been set with this client then the message will be encrypted using
-   normal session keys. Private messages are special packets in SILC
-   network hence we need this own function for them. This is similiar
-   to silc_client_packet_send_to_channel except that we send private
-   message. The `data' is the private message. If the `force_send' is
-   TRUE the packet is sent immediately. */
-
-void silc_client_send_private_message(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry,
-                                     unsigned char *data, 
-                                     unsigned int data_len, 
-                                     int force_send)
-{
-  SilcSocketConnection sock = conn->sock;
-  SilcBuffer buffer;
-  SilcPacketContext packetdata;
-  unsigned int nick_len;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  SILC_LOG_DEBUG(("Sending private message"));
-
-  /* Create private message payload */
-  nick_len = strlen(conn->nickname);
-  buffer = silc_buffer_alloc(2 + nick_len + data_len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-  silc_buffer_format(buffer,
-                    SILC_STR_UI_SHORT(nick_len),
-                    SILC_STR_UI_XNSTRING(conn->nickname,
-                                         nick_len),
-                    SILC_STR_UI_XNSTRING(data, data_len),
-                    SILC_STR_END);
-
-  /* If we don't have private message specific key then private messages
-     are just as any normal packet thus call normal packet sending.  If
-     the key exist then the encryption process is a bit different and
-     will be done in the rest of this function. */
-  if (!client_entry->send_key) {
-    silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
-                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                           buffer->data, buffer->len, force_send);
-    goto out;
-  }
-
-  /* We have private message specific key */
-
-  /* Get data used in the encryption */
-  cipher = client_entry->send_key;
-  hmac = conn->hmac;
-
-  /* Set the packet context pointers. */
-  packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
-  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
-  packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
-  packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.dst_id_type = SILC_ID_CLIENT;
-  packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          buffer->len);
-  
-  packetdata.buffer = sock->outbuf;
-
-  /* Encrypt payload of the packet. Encrypt with private message specific
-     key */
-  cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
-                         buffer->len, cipher->iv);
-      
-  /* Put the actual encrypted payload data into the buffer. */
-  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Encrypt the header and padding of the packet. */
-  cipher = conn->send_key;
-  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-  silc_free(packetdata.dst_id);
-
- out:
-  silc_free(buffer);
-}     
-
 /* Closes connection to remote end. Free's all allocated data except
    for some information such as nickname etc. that are valid at all time. */
 
@@ -1281,218 +1060,108 @@ void silc_client_error_by_server(SilcClient client,
   silc_free(msg);
 }
 
-/* Called when notify is received and some async operation (such as command)
-   is required before processing the notify message. This calls again the
-   silc_client_notify_by_server and reprocesses the original notify packet. */
+/* Processes the received new Client ID from server. Old Client ID is
+   deleted from cache and new one is added. */
 
-static void silc_client_notify_by_server_pending(void *context)
+void silc_client_receive_new_id(SilcClient client,
+                               SilcSocketConnection sock,
+                               SilcIDPayload idp)
 {
-  SilcPacketContext *p = (SilcPacketContext *)context;
-  silc_client_notify_by_server(p->context, p->sock, p);
-}
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  int connecting = FALSE;
 
-/* Destructor for the pending command callback */
+  if (!conn->local_entry)
+    connecting = TRUE;
 
-static void silc_client_notify_by_server_destructor(void *context)
-{
-  silc_packet_context_free((SilcPacketContext *)context);
-}
+  /* Delete old ID from ID cache */
+  silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
+  
+  /* Save the new ID */
+  if (conn->local_id)
+    silc_free(conn->local_id);
+  if (conn->local_id_data)
+    silc_free(conn->local_id_data);
 
-/* Resolve client information from server by Client ID. */
+  conn->local_id = silc_id_payload_get_id(idp);
+  conn->local_id_data = silc_id_payload_get_data(idp);
+  conn->local_id_data_len = silc_id_payload_get_len(idp);;
 
-static void silc_client_notify_by_server_resolve(SilcClient client,
-                                                SilcClientConnection conn,
-                                                SilcPacketContext *packet,
-                                                SilcClientID *client_id)
-{
-  SilcPacketContext *p = silc_packet_context_dup(packet);
-  SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-
-  p->context = (void *)client;
-  p->sock = conn->sock;
-
-  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
-                          1, 3, idp->data, idp->len);
-  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
-                             silc_client_notify_by_server_destructor,
-                             silc_client_notify_by_server_pending, p);
-  silc_buffer_free(idp);
-}
+  if (!conn->local_entry)
+    conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
+
+  conn->local_entry->nickname = conn->nickname;
+  if (!conn->local_entry->username) {
+    conn->local_entry->username = 
+      silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
+                 sizeof(conn->local_entry->username));
+    sprintf(conn->local_entry->username, "%s@%s", client->username,
+           client->hostname);
+  }
+  conn->local_entry->server = strdup(conn->remote_host);
+  conn->local_entry->id = conn->local_id;
+  
+  /* Put it to the ID cache */
+  silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
+                  conn->local_id, (void *)conn->local_entry, TRUE);
 
-/* Received notify message from server */
+  /* Notify application of successful connection. We do it here now that
+     we've received the Client ID and are allowed to send traffic. */
+  if (connecting)
+    client->ops->connect(client, conn, TRUE);
+}
 
-void silc_client_notify_by_server(SilcClient client,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet)
+/* Processed received Channel ID for a channel. This is called when client
+   joins to channel and server replies with channel ID. The ID is cached. 
+   Returns the created channel entry. */
+
+SilcChannelEntry silc_client_new_channel_id(SilcClient client,
+                                           SilcSocketConnection sock,
+                                           char *channel_name,
+                                           unsigned int mode, 
+                                           SilcIDPayload idp)
 {
-  SilcBuffer buffer = packet->buffer;
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcNotifyPayload payload;
-  SilcNotifyType type;
-  SilcArgumentPayload args;
-
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
-  SilcClientEntry client_entry;
-  SilcClientEntry client_entry2;
   SilcChannelEntry channel;
-  SilcChannelUser chu;
-  SilcIDCacheEntry id_cache = NULL;
-  unsigned char *tmp;
-  unsigned int tmp_len, mode;
-
-  payload = silc_notify_payload_parse(buffer);
-  if (!payload)
-    goto out;
-
-  type = silc_notify_get_type(payload);
-  args = silc_notify_get_args(payload);
-  if (!args)
-    goto out;
-
-  switch(type) {
-  case SILC_NOTIFY_TYPE_NONE:
-    /* Notify application */
-    client->ops->notify(client, conn, type, 
-                       silc_argument_get_arg_type(args, 1, NULL));
-    break;
 
-  case SILC_NOTIFY_TYPE_INVITE:
-    /* 
-     * Someone invited me to a channel. Find Client and Channel entries
-     * for the application.
-     */
-    
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry and if not found query it */
-    client_entry = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry) {
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
-      goto out;
-    }
-
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel_id)
-      goto out;
-
-    /* XXX Will ALWAYS fail because currently we don't have way to resolve
-       channel information for channel that we're not joined to. */
-    /* XXX ways to fix: use (extended) LIST command, or define the channel
-       name to the notfy type when name resolving is not mandatory. */
-    /* Find channel entry */
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      goto out;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Notify application */
-    client->ops->notify(client, conn, type, client_entry, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_JOIN:
-    /*
-     * Someone has joined to a channel. Get their ID and nickname and
-     * cache them for later use.
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry and if not found query it */
-    client_entry = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry) {
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
-      goto out;
-    }
-
-    /* If nickname or username hasn't been resolved, do so */
-    if (!client_entry->nickname || !client_entry->username) {
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
-      goto out;
-    }
+  SILC_LOG_DEBUG(("New channel ID"));
 
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
+  channel = silc_calloc(1, sizeof(*channel));
+  channel->channel_name = channel_name;
+  channel->id = silc_id_payload_get_id(idp);
+  channel->mode = mode;
+  silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
 
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel_id)
-      goto out;
+  conn->current_channel = channel;
 
-    /* Get channel entry */
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
+  /* Put it to the ID cache */
+  silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
+                  (void *)channel->id, (void *)channel, TRUE);
 
-    channel = (SilcChannelEntry)id_cache->context;
+  return channel;
+}
 
-    /* Add client to channel */
-    chu = silc_calloc(1, sizeof(*chu));
-    chu->client = client_entry;
-    silc_list_add(channel->clients, chu);
+/* Removes a client entry from all channel it has joined. This really is
+   a performance killer (client_entry should have pointers to channel 
+   entry list). */
 
-    /* XXX add support for multiple same nicks on same channel. Check
-       for them here */
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, channel);
-    break;
+  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
+                              SILC_ID_CHANNEL, &list))
+    return;
 
-  case SILC_NOTIFY_TYPE_LEAVE:
-    /*
-     * Someone has left a channel. We will remove it from the channel but
-     * we'll keep it in the cache in case we'll need it later.
-     */
+  silc_idcache_list_first(list, &id_cache);
+  channel = (SilcChannelEntry)id_cache->context;
+  
+  while (channel) {
     
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
     /* Remove client from channel */
     silc_list_start(channel->clients);
     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
@@ -1503,763 +1172,19 @@ void silc_client_notify_by_server(SilcClient client,
       }
     }
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_SIGNOFF:
-    /*
-     * Someone left SILC. We'll remove it from all channels and from cache.
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-
-    /* Remove from all channels */
-    silc_client_remove_from_channels(client, conn, client_entry);
-
-    /* Remove from cache */
-    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
-                          client_entry->id);
-
-    /* Get signoff message */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (tmp_len > 128)
-      tmp = NULL;
-
-    /* Notify application */
-    client->ops->notify(client, conn, type, client_entry, tmp);
-
-    /* Free data */
-    if (client_entry->nickname)
-      silc_free(client_entry->nickname);
-    if (client_entry->server)
-      silc_free(client_entry->server);
-    if (client_entry->id)
-      silc_free(client_entry->id);
-    if (client_entry->send_key)
-      silc_cipher_free(client_entry->send_key);
-    if (client_entry->receive_key)
-      silc_cipher_free(client_entry->receive_key);
-    break;
-
-  case SILC_NOTIFY_TYPE_TOPIC_SET:
-    /*
-     * Someone set the topic on a channel.
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-
-    /* Get topic */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
+    if (!silc_idcache_list_next(list, &id_cache))
       break;
-
+    
     channel = (SilcChannelEntry)id_cache->context;
+  }
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, tmp, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_NICK_CHANGE:
-    /*
-     * Someone changed their nickname. If we don't have entry for the new
-     * ID we will query it and return here after it's done. After we've
-     * returned we fetch the old entry and free it and notify the 
-     * application.
-     */
-
-    /* Get new Client ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Ignore my ID */
-    if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
-      break;
+  silc_idcache_list_free(list);
+}
 
-    /* Find Client entry and if not found query it */
-    client_entry2 = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry2) {
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
-      goto out;
-    }
-    silc_free(client_id);
-
-    /* Get old Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find old Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-
-    /* Remove the old from cache */
-    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
-                          client_entry->id);
-
-    /* Replace old ID entry with new one on all channels. */
-    silc_client_replace_from_channels(client, conn, client_entry,
-                                     client_entry2);
-
-    /* Notify application */
-    client->ops->notify(client, conn, type, client_entry, client_entry2);
-
-    /* Free data */
-    if (client_entry->nickname)
-      silc_free(client_entry->nickname);
-    if (client_entry->server)
-      silc_free(client_entry->server);
-    if (client_entry->id)
-      silc_free(client_entry->id);
-    if (client_entry->send_key)
-      silc_cipher_free(client_entry->send_key);
-    if (client_entry->receive_key)
-      silc_cipher_free(client_entry->receive_key);
-    break;
-
-  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
-    /*
-     * Someone changed a channel mode
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-
-    /* Get the mode */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    SILC_GET32_MSB(mode, tmp);
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Save the new mode */
-    channel->mode = mode;
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, mode, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
-    /*
-     * Someone changed user's mode on a channel
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-
-    /* Get the mode */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    SILC_GET32_MSB(mode, tmp);
-
-    /* Get target Client ID */
-    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    silc_free(client_id);
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find target Client entry */
-    client_entry2 = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry2)
-      goto out;
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Save the mode */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == client_entry) {
-       chu->mode = mode;
-       break;
-      }
-    }
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, mode, 
-                       client_entry2, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_MOTD:
-    /*
-     * Received Message of the day
-     */
-
-    /* Get motd */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    
-    /* Notify application */
-    client->ops->notify(client, conn, type, tmp);
-    break;
-
-  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
-    /*
-     * Router has enforced a new ID to a channel. Let's change the old
-     * ID to the one provided here.
-     */
-
-    /* Get the old ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel_id)
-      goto out;
-    
-    /* Get the channel entry */
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Free the old ID */
-    silc_free(channel_id);
-    silc_free(channel->id);
-
-    /* Get the new ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-    channel->id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel->id)
-      goto out;
-
-    id_cache->id = (void *)channel->id;
-
-    /* Notify application */
-    client->ops->notify(client, conn, type, channel, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_KICKED:
-    /*
-     * A client (maybe me) was kicked from a channel
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
-
-    /* Find Client entry */
-    client_entry = 
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Get comment */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, tmp, channel);
-
-    /* If I was kicked from channel, remove the channel */
-    if (client_entry == conn->local_entry) {
-      if (conn->current_channel == channel)
-       conn->current_channel = NULL;
-      silc_idcache_del_by_id(conn->channel_cache, 
-                            SILC_ID_CHANNEL, channel->id);
-      silc_free(channel->channel_name);
-      silc_free(channel->id);
-      silc_free(channel->key);
-      silc_cipher_free(channel->channel_key);
-      silc_free(channel);
-    }
-    break;
-    
-  default:
-    break;
-  }
-
- out:
-  silc_notify_payload_free(payload);
-  if (client_id)
-    silc_free(client_id);
-  if (channel_id)
-    silc_free(channel_id);
-}
-
-/* Processes the received new Client ID from server. Old Client ID is
-   deleted from cache and new one is added. */
-
-void silc_client_receive_new_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               SilcIDPayload idp)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  int connecting = FALSE;
-
-  if (!conn->local_entry)
-    connecting = TRUE;
-
-  /* Delete old ID from ID cache */
-  silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
-  
-  /* Save the new ID */
-  if (conn->local_id)
-    silc_free(conn->local_id);
-  if (conn->local_id_data)
-    silc_free(conn->local_id_data);
-
-  conn->local_id = silc_id_payload_get_id(idp);
-  conn->local_id_data = silc_id_payload_get_data(idp);
-  conn->local_id_data_len = silc_id_payload_get_len(idp);;
-
-  if (!conn->local_entry)
-    conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
-
-  conn->local_entry->nickname = conn->nickname;
-  if (!conn->local_entry->username) {
-    conn->local_entry->username = 
-      silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
-                 sizeof(conn->local_entry->username));
-    sprintf(conn->local_entry->username, "%s@%s", client->username,
-           client->hostname);
-  }
-  conn->local_entry->server = strdup(conn->remote_host);
-  conn->local_entry->id = conn->local_id;
-  
-  /* Put it to the ID cache */
-  silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
-                  conn->local_id, (void *)conn->local_entry, TRUE);
-
-  /* Notify application of successful connection. We do it here now that
-     we've received the Client ID and are allowed to send traffic. */
-  if (connecting)
-    client->ops->connect(client, conn, TRUE);
-}
-
-/* Processed received Channel ID for a channel. This is called when client
-   joins to channel and server replies with channel ID. The ID is cached. */
-
-void silc_client_new_channel_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               char *channel_name,
-                               unsigned int mode, SilcIDPayload idp)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcChannelEntry channel;
-
-  SILC_LOG_DEBUG(("New channel ID"));
-
-  channel = silc_calloc(1, sizeof(*channel));
-  channel->channel_name = channel_name;
-  channel->id = silc_id_payload_get_id(idp);
-  channel->mode = mode;
-  silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
-
-  conn->current_channel = channel;
-
-  /* Put it to the ID cache */
-  silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
-                  (void *)channel->id, (void *)channel, TRUE);
-}
-
-/* Saves channel key from encoded `key_payload'. This is used when we
-   receive Channel Key Payload and when we are processing JOIN command 
-   reply. */
-
-void silc_client_save_channel_key(SilcClientConnection conn,
-                                 SilcBuffer key_payload, 
-                                 SilcChannelEntry channel)
-{
-  unsigned char *id_string, *key, *cipher;
-  unsigned int tmp_len;
-  SilcChannelID *id;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcChannelKeyPayload payload;
-
-  payload = silc_channel_key_payload_parse(key_payload);
-  if (!payload)
-    return;
-
-  id_string = silc_channel_key_get_id(payload, &tmp_len);
-  if (!id_string) {
-    silc_channel_key_payload_free(payload);
-    return;
-  }
-
-  id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
-  if (!id) {
-    silc_channel_key_payload_free(payload);
-    return;
-  }
-
-  /* Find channel. */
-  if (!channel) {
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      goto out;
-    
-    /* Get channel entry */
-    channel = (SilcChannelEntry)id_cache->context;
-  }
-
-  /* Save the key */
-  key = silc_channel_key_get_key(payload, &tmp_len);
-  cipher = silc_channel_key_get_cipher(payload, NULL);
-  channel->key_len = tmp_len * 8;
-  channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
-  memcpy(channel->key, key, tmp_len);
-
-  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
-    conn->client->ops->say(conn->client, conn,
-                    "Cannot talk to channel: unsupported cipher %s", cipher);
-    goto out;
-  }
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       key, channel->key_len);
-
-  /* Client is now joined to the channel */
-  channel->on_channel = TRUE;
-
- out:
-  silc_free(id);
-  silc_channel_key_payload_free(payload);
-}
-
-/* Processes received key for channel. The received key will be used
-   to protect the traffic on the channel for now on. Client must receive
-   the key to the channel before talking on the channel is possible. 
-   This is the key that server has generated, this is not the channel
-   private key, it is entirely local setting. */
-
-void silc_client_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet)
-{
-  SILC_LOG_DEBUG(("Received key for channel"));
-
-  /* Save the key */
-  silc_client_save_channel_key(sock->user_data, packet, NULL);
-}
-
-/* Process received message to a channel (or from a channel, really). This
-   decrypts the channel message with channel specific key and parses the
-   channel payload. Finally it displays the message on the screen. */
-
-void silc_client_channel_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer buffer = packet->buffer;
-  SilcChannelPayload payload = NULL;
-  SilcChannelID *id = NULL;
-  SilcChannelEntry channel;
-  SilcChannelUser chu;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcClientID *client_id = NULL;
-  int found = FALSE;
-  unsigned int block_len;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL)
-    goto out;
-
-  client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                            SILC_ID_CLIENT);
-  if (!client_id)
-    goto out;
-  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
-  if (!id)
-    goto out;
-
-  /* Find the channel entry from channels on this connection */
-  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                  SILC_ID_CHANNEL, &id_cache))
-    goto out;
-
-  channel = (SilcChannelEntry)id_cache->context;
-
-  /* Decrypt the channel message payload. Push the IV out of the way,
-     since it is not encrypted (after pushing buffer->tail has the IV). */
-  block_len = silc_cipher_get_block_len(channel->channel_key);
-  silc_buffer_push_tail(buffer, block_len);
-  channel->channel_key->cipher->decrypt(channel->channel_key->context,
-                                       buffer->data, buffer->data,
-                                       buffer->len, buffer->tail);
-  silc_buffer_pull_tail(buffer, block_len);
-
-  /* Parse the channel message payload */
-  payload = silc_channel_payload_parse(buffer);
-  if (!payload)
-    goto out;
-
-  /* Find client entry */
-  silc_list_start(channel->clients);
-  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-    if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
-      found = TRUE;
-      break;
-    }
-  }
-
-  /* Pass the message to application */
-  client->ops->channel_message(client, conn, found ? chu->client : NULL,
-                              channel, silc_channel_get_data(payload, NULL));
-
- out:
-  if (id)
-    silc_free(id);
-  if (client_id)
-    silc_free(client_id);
-  if (payload)
-    silc_channel_payload_free(payload);
-}
-
-/* Private message received. This processes the private message and
-   finally displays it on the screen. */
-
-void silc_client_private_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer buffer = packet->buffer;
-  SilcIDCacheEntry id_cache;
-  SilcClientID *remote_id = NULL;
-  SilcClientEntry remote_client;
-  unsigned short nick_len;
-  unsigned char *nickname, *message = NULL;
-  int ret;
-
-  if (packet->src_id_type != SILC_ID_CLIENT)
-    goto out;
-
-  /* Get nickname */
-  ret = silc_buffer_unformat(buffer, 
-                            SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
-                            SILC_STR_END);
-  if (ret == -1)
-    return;
-
-  silc_buffer_pull(buffer, 2 + nick_len);
-
-  message = silc_calloc(buffer->len + 1, sizeof(char));
-  memcpy(message, buffer->data, buffer->len);
-
-  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
-                            SILC_ID_CLIENT);
-  if (!remote_id)
-    goto out;
-
-  /* Check whether we know this client already */
-  if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
-                                  SILC_ID_CLIENT, &id_cache))
-    {
-      /* Allocate client entry */
-      remote_client = silc_calloc(1, sizeof(*remote_client));
-      remote_client->id = remote_id;
-      silc_parse_nickname(nickname, &remote_client->nickname, 
-                         &remote_client->server, &remote_client->num);
-      
-      /* Save the client to cache */
-      silc_idcache_add(conn->client_cache, remote_client->nickname,
-                      SILC_ID_CLIENT, remote_client->id, remote_client, 
-                      TRUE);
-    } else {
-      remote_client = (SilcClientEntry)id_cache->context;
-    }
-
-  /* Pass the private message to application */
-  client->ops->private_message(client, conn, remote_client, message);
-
-  /* See if we are away (gone). If we are away we will reply to the
-     sender with the set away message. */
-  if (conn->away && conn->away->away) {
-    /* If it's me, ignore */
-    if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
-      goto out;
-
-    /* Send the away message */
-    silc_client_send_private_message(client, conn, remote_client,
-                                    conn->away->away,
-                                    strlen(conn->away->away), TRUE);
-  }
-
- out:
-  if (remote_id)
-    silc_free(remote_id);
-
-  if (message) {
-    memset(message, 0, buffer->len);
-    silc_free(message);
-  }
-  silc_free(nickname);
-}
-
-/* Removes a client entry from all channel it has joined. This really is
-   a performance killer (client_entry should have pointers to channel 
-   entry list). */
-
-void silc_client_remove_from_channels(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry)
-{
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
-  SilcChannelEntry channel;
-  SilcChannelUser chu;
-
-  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
-                              SILC_ID_CHANNEL, &list))
-    return;
-
-  silc_idcache_list_first(list, &id_cache);
-  channel = (SilcChannelEntry)id_cache->context;
-  
-  while (channel) {
-    
-    /* Remove client from channel */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == client_entry) {
-       silc_list_del(channel->clients, chu);
-       silc_free(chu);
-       break;
-      }
-    }
-
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
-    
-    channel = (SilcChannelEntry)id_cache->context;
-  }
-
-  silc_idcache_list_free(list);
-}
-
-/* Replaces `old' client entries from all channels to `new' client entry.
-   This can be called for example when nickname changes and old ID entry
-   is replaced from ID cache with the new one. If the old ID entry is only
-   updated, then this fucntion needs not to be called. */
+/* Replaces `old' client entries from all channels to `new' client entry.
+   This can be called for example when nickname changes and old ID entry
+   is replaced from ID cache with the new one. If the old ID entry is only
+   updated, then this fucntion needs not to be called. */
 
 void silc_client_replace_from_channels(SilcClient client, 
                                       SilcClientConnection conn,
@@ -2375,502 +1300,52 @@ char *silc_client_chumode_char(unsigned int mode)
   return strdup(string);
 }
 
-/* Function that actually employes the received private message key */
-
-static void silc_client_private_message_key_cb(SilcClient client,
-                                              SilcClientConnection conn,
-                                              SilcClientEntry *clients,
-                                              unsigned int clients_count,
-                                              void *context)
-{
-  SilcPacketContext *packet = (SilcPacketContext *)context;
-  unsigned char *key;
-  unsigned short key_len;
-  unsigned char *cipher;
-  int ret;
-
-  if (!clients)
-    goto out;
-
-  /* Parse the private message key payload */
-  ret = silc_buffer_unformat(packet->buffer,
-                            SILC_STR_UI16_NSTRING(&key, &key_len),
-                            SILC_STR_UI16_STRING(&cipher),
-                            SILC_STR_END);
-  if (!ret)
-    goto out;
-
-  if (key_len > packet->buffer->len)
-    goto out;
-
-  /* Now take the key in use */
-  if (!silc_client_add_private_message_key(client, conn, clients[0],
-                                          cipher, key, key_len, FALSE))
-    goto out;
-
-  /* Print some info for application */
-  client->ops->say(client, conn, 
-                  "Received private message key from %s%s%s %s%s%s", 
-                  clients[0]->nickname,
-                  clients[0]->server ? "@" : "",
-                  clients[0]->server ? clients[0]->server : "",
-                  clients[0]->username ? "(" : "",
-                  clients[0]->username ? clients[0]->username : "",
-                  clients[0]->username ? ")" : "");
-
- out:
-  silc_packet_context_free(packet);
-}
-
-/* Processes incoming Private Message Key payload. The libary always
-   accepts the key and takes it into use. */
-
-void silc_client_private_message_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcPacketContext *packet)
-{
-  SilcClientID *remote_id;
-
-  if (packet->src_id_type != SILC_ID_CLIENT)
-    return;
-
-  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
-                            SILC_ID_CLIENT);
-  if (!remote_id)
-    return;
-
-  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
-                                      silc_client_private_message_key_cb,
-                                      silc_packet_context_dup(packet));
-  silc_free(remote_id);
-}
-
-/* Adds private message key to the client library. The key will be used to
-   encrypt all private message between the client and the remote client
-   indicated by the `client_entry'. If the `key' is NULL and the boolean
-   value `generate_key' is TRUE the library will generate random key.
-   The `key' maybe for example pre-shared-key, passphrase or similar.
-   The `cipher' MAY be provided but SHOULD be NULL to assure that the
-   requirements of the SILC protocol are met. The API, however, allows
-   to allocate any cipher.
-
-   It is not necessary to set key for normal private message usage. If the
-   key is not set then the private messages are encrypted using normal
-   session keys. Setting the private key, however, increases the security. 
-
-   Returns FALSE if the key is already set for the `client_entry', TRUE
-   otherwise. */
-
-int silc_client_add_private_message_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcClientEntry client_entry,
-                                       char *cipher,
-                                       unsigned char *key,
-                                       unsigned int key_len,
-                                       int generate_key)
-{
-  unsigned char private_key[32];
-  unsigned int len;
-  int i;
-  SilcSKEKeyMaterial *keymat;
-
-  assert(client_entry);
-
-  /* Return FALSE if key already set */
-  if (client_entry->send_key && client_entry->receive_key)
-    return FALSE;
-
-  if (!cipher)
-    cipher = "aes-256-cbc";
-
-  /* Check the requested cipher */
-  if (!silc_cipher_is_supported(cipher))
-    return FALSE;
-
-  /* Generate key if not provided */
-  if (!key && generate_key == TRUE) {
-    len = 32;
-    for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
-    key = private_key;
-    key_len = len;
-    client_entry->generated = TRUE;
-  }
-
-  /* Save the key */
-  client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
-  memcpy(client_entry->key, key, key_len);
-  client_entry->key_len = key_len;
-
-  /* Produce the key material as the protocol defines */
-  keymat = silc_calloc(1, sizeof(*keymat));
-  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
-                                        client->md5hash, keymat) 
-      != SILC_SKE_STATUS_OK)
-    return FALSE;
-
-  /* Allocate the ciphers */
-  silc_cipher_alloc(cipher, &client_entry->send_key);
-  silc_cipher_alloc(cipher, &client_entry->receive_key);
-
-  /* Set the keys */
-  silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
-                     keymat->enc_key_len);
-  silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
-  silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
-                     keymat->enc_key_len);
-  silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
-
-  /* Free the key material */
-  silc_ske_free_key_material(keymat);
-
-  return TRUE;
-}
-
-/* Same as above but takes the key material from the SKE key material
-   structure. This structure is received if the application uses the
-   silc_client_send_key_agreement to negotiate the key material. The
-   `cipher' SHOULD be provided as it is negotiated also in the SKE
-   protocol. */
-
-int silc_client_add_private_message_key_ske(SilcClient client,
-                                           SilcClientConnection conn,
-                                           SilcClientEntry client_entry,
-                                           char *cipher,
-                                           SilcSKEKeyMaterial *key)
-{
-  assert(client_entry);
-
-  /* Return FALSE if key already set */
-  if (client_entry->send_key && client_entry->receive_key)
-    return FALSE;
-
-  if (!cipher)
-    cipher = "aes-256-cbc";
-
-  /* Check the requested cipher */
-  if (!silc_cipher_is_supported(cipher))
-    return FALSE;
-
-  /* Allocate the ciphers */
-  silc_cipher_alloc(cipher, &client_entry->send_key);
-  silc_cipher_alloc(cipher, &client_entry->receive_key);
-
-  /* Set the keys */
-  silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
-                     key->enc_key_len);
-  silc_cipher_set_iv(client_entry->send_key, key->send_iv);
-  silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
-                     key->enc_key_len);
-  silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+/* 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). */
 
-  return TRUE;
-}
-
-/* Sends private message key payload to the remote client indicated by
-   the `client_entry'. If the `force_send' is TRUE the packet is sent
-   immediately. Returns FALSE if error occurs, TRUE otherwise. The
-   application should call this function after setting the key to the
-   client.
-
-   Note that the key sent using this function is sent to the remote client
-   through the SILC network. The packet is protected using normal session
-   keys. */
-
-int silc_client_send_private_message_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcClientEntry client_entry,
-                                        int force_send)
+SILC_TASK_CALLBACK_GLOBAL(silc_client_failure_callback)
 {
-  SilcSocketConnection sock = conn->sock;
-  SilcBuffer buffer;
-  int cipher_len;
+  SilcClientFailureContext *f = (SilcClientFailureContext *)context;
 
-  if (!client_entry->send_key || !client_entry->key)
-    return FALSE;
-
-  SILC_LOG_DEBUG(("Sending private message key"));
-
-  cipher_len = strlen(client_entry->send_key->cipher->name);
-
-  /* Create private message key payload */
-  buffer = silc_buffer_alloc(2 + client_entry->key_len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-  silc_buffer_format(buffer,
-                    SILC_STR_UI_SHORT(client_entry->key_len),
-                    SILC_STR_UI_XNSTRING(client_entry->key, 
-                                         client_entry->key_len),
-                    SILC_STR_UI_SHORT(cipher_len),
-                    SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
-                                         cipher_len),
-                    SILC_STR_END);
-
-  /* Send the packet */
-  silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
-                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                         buffer->data, buffer->len, force_send);
-  silc_free(buffer);
-
-  return TRUE;
-}
-
-/* Removes the private message from the library. The key won't be used
-   after this to protect the private messages with the remote `client_entry'
-   client. Returns FALSE on error, TRUE otherwise. */
-
-int silc_client_del_private_message_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcClientEntry client_entry)
-{
-  assert(client_entry);
-
-  if (!client_entry->send_key && !client_entry->receive_key)
-    return FALSE;
-
-  silc_cipher_free(client_entry->send_key);
-  silc_cipher_free(client_entry->receive_key);
-
-  if (client_entry->key) {
-    memset(client_entry->key, 0, client_entry->key_len);
-    silc_free(client_entry->key);
+  if (f->sock->protocol) {
+    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+    f->sock->protocol->execute(f->client->timeout_queue, 0,
+                              f->sock->protocol, f->sock->sock, 0, 0);
+    
+    /* Notify application */
+    f->client->ops->failure(f->client, f->sock->user_data, f->sock->protocol,
+                           (void *)f->failure);
   }
 
-  client_entry->send_key = NULL;
-  client_entry->receive_key = NULL;
-  client_entry->key = NULL;
-
-  return TRUE;
+  silc_free(f);
 }
 
-/* Returns array of set private message keys associated to the connection
-   `conn'. Returns allocated SilcPrivateMessageKeys array and the array
-   count to the `key_count' argument. The array must be freed by the caller
-   by calling the silc_client_free_private_message_keys function. Note: 
-   the keys returned in the array is in raw format. It might not be desired
-   to show the keys as is. The application might choose not to show the keys
-   at all or to show the fingerprints of the keys. */
+/* Registers failure timeout to process the received failure packet
+   with timeout. */
 
-SilcPrivateMessageKeys
-silc_client_list_private_message_keys(SilcClient client,
-                                     SilcClientConnection conn,
-                                     unsigned int *key_count)
+void silc_client_process_failure(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
 {
-  SilcPrivateMessageKeys keys;
-  unsigned int count = 0;
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
-  SilcClientEntry entry;
-
-  if (!silc_idcache_find_by_id(conn->client_cache, SILC_ID_CACHE_ANY, 
-                              SILC_ID_CLIENT, &list))
-    return NULL;
-
-  if (!silc_idcache_list_count(list)) {
-    silc_idcache_list_free(list);
-    return NULL;
-  }
-
-  keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
-
-  silc_idcache_list_first(list, &id_cache);
-  while (id_cache) {
-    entry = (SilcClientEntry)id_cache->context;
-
-    if (entry->send_key) {
-      keys[count].client_entry = entry;
-      keys[count].cipher = entry->send_key->cipher->name;
-      keys[count].key = entry->generated == FALSE ? entry->key : NULL;
-      keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
-      count++;
-    }
+  SilcClientFailureContext *f;
+  unsigned int failure = 0;
 
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
+  if (sock->protocol) {
+    if (packet->buffer->len >= 4)
+      SILC_GET32_MSB(failure, packet->buffer->data);
+
+    f = silc_calloc(1, sizeof(*f));
+    f->client = client;
+    f->sock = sock;
+    f->failure = failure;
+
+    /* We will wait 5 seconds to process this failure packet */
+    silc_task_register(client->timeout_queue, sock->sock,
+                      silc_client_failure_callback, (void *)f, 5, 0,
+                      SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
   }
-
-  if (key_count)
-    *key_count = count;
-
-  return keys;
-}
-
-/* Frees the SilcPrivateMessageKeys array returned by the function
-   silc_client_list_private_message_keys. */
-
-void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
-                                          unsigned int key_count)
-{
-  silc_free(keys);
-}
-
-/* Adds private key for channel. This may be set only if the channel's mode
-   mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
-   mode is not set. When channel has private key then the messages are
-   encrypted using that key. All clients on the channel must also know the
-   key in order to decrypt the messages. However, it is possible to have
-   several private keys per one channel. In this case only some of the
-   clients on the channel may now the one key and only some the other key.
-
-   The private key for channel is optional. If it is not set then the
-   channel messages are encrypted using the channel key generated by the
-   server. However, setting the private key (or keys) for the channel 
-   significantly adds security. If more than one key is set the library
-   will automatically try all keys at the message decryption phase. Note:
-   setting many keys slows down the decryption phase as all keys has to
-   be tried in order to find the correct decryption key. However, setting
-   a few keys does not have big impact to the decryption performace. 
-
-   NOTE: that this is entirely local setting. The key set using this function
-   is not sent to the network at any phase.
-
-   NOTE: If the key material was originated by the SKE protocol (using
-   silc_client_send_key_agreement) then the `key' MUST be the
-   key->send_enc_key as this is dictated by the SILC protocol. However,
-   currently it is not expected that the SKE key material would be used
-   as channel private key. However, this API allows it. */
-
-int silc_client_add_channel_private_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcChannelEntry channel,
-                                       char *cipher,
-                                       unsigned char *key,
-                                       unsigned int key_len)
-{
-
-  return TRUE;
-}
-
-/* Removes all private keys from the `channel'. The old channel key is used
-   after calling this to protect the channel messages. Returns FALSE on
-   on error, TRUE otherwise. */
-
-int silc_client_del_channel_private_keys(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcChannelEntry channel)
-{
-
-  return TRUE;
-}
-
-/* Removes and frees private key `key' from the channel `channel'. The `key'
-   is retrieved by calling the function silc_client_list_channel_private_keys.
-   The key is not used after this. If the key was last private key then the
-   old channel key is used hereafter to protect the channel messages. This
-   returns FALSE on error, TRUE otherwise. */
-
-int silc_client_del_channel_private_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcChannelEntry channel,
-                                       SilcChannelPrivateKey key)
-{
-
-  return TRUE;
-}
-
-/* Returns array (pointers) of private keys associated to the `channel'.
-   The caller must free the array by calling the function
-   silc_client_free_channel_private_keys. The pointers in the array may be
-   used to delete the specific key by giving the pointer as argument to the
-   function silc_client_del_channel_private_key. */
-
-SilcChannelPrivateKey *
-silc_client_list_channel_private_keys(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcChannelEntry channel,
-                                     unsigned int key_count)
-{
-
-  return NULL;
-}
-
-/* Frees the SilcChannelPrivateKey array. */
-
-void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
-                                          unsigned int key_count)
-{
-
-}
-
-/* Sends key agreement request to the remote client indicated by the
-   `client_entry'. If the caller provides the `hostname' and the `port'
-   arguments then the library will bind the client to that hostname and
-   that port for the key agreement protocol. It also sends the `hostname'
-   and the `port' in the key agreement packet to the remote client. This
-   would indicate that the remote client may initiate the key agreement
-   protocol to the `hostname' on the `port'.
-
-   If the `hostname' and `port' is not provided then empty key agreement
-   packet is sent to the remote client. The remote client may reply with
-   the same packet including its hostname and port. If the library receives
-   the reply from the remote client the `key_agreement' client operation
-   callback will be called to verify whether the user wants to perform the
-   key agreement or not. 
-
-   NOTE: If the application provided the `hostname' and the `port' and the 
-   remote side initiates the key agreement protocol it is not verified
-   from the user anymore whether the protocol should be executed or not.
-   By setting the `hostname' and `port' the user gives permission to
-   perform the protocol (we are responder in this case).
-
-   NOTE: If the remote side decides not to initiate the key agreement
-   or decides not to reply with the key agreement packet then we cannot
-   perform the key agreement at all. If the key agreement protocol is
-   performed the `completion' callback with the `context' will be called.
-   If remote side decides to ignore the request the `completion' will never
-   be called and the caller is responsible of freeing the `context' memory. 
-   The application can do this by setting, for example, timeout. */
-
-void silc_client_send_key_agreement(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcClientEntry client_entry,
-                                   char *hostname,
-                                   int port,
-                                   SilcKeyAgreementCallback completion,
-                                   void *context)
-{
-
-}
-
-/* Performs the actual key agreement protocol. Application may use this
-   to initiate the key agreement protocol. This can be called for example
-   after the application has received the `key_agreement' client operation,
-   and did not return TRUE from it.
-
-   The `hostname' is the remote hostname (or IP address) and the `port'
-   is the remote port. The `completion' callblack with the `context' will
-   be called after the key agreement protocol.
-   
-   NOTE: If the application returns TRUE in the `key_agreement' client
-   operation the library will automatically start the key agreement. In this
-   case the application must not call this function. However, application
-   may choose to just ignore the `key_agreement' client operation (and
-   merely just print information about it on the screen) and call this
-   function when the user whishes to do so (by, for example, giving some
-   specific command). Thus, the API provides both, automatic and manual
-   initiation of the key agreement. Calling this function is the manual
-   initiation and returning TRUE in the `key_agreement' client operation
-   is the automatic initiation. */
-
-void silc_client_perform_key_agreement(SilcClient client,
-                                      SilcClientConnection conn,
-                                      SilcClientEntry client_entry,
-                                      char *hostname,
-                                      int port,
-                                      SilcKeyAgreementCallback completion,
-                                      void *context)
-{
-
-}
-
-/* This function can be called to unbind the hostname and the port for
-   the key agreement protocol. However, this function has effect only 
-   before the key agreement protocol has been performed. After it has
-   been performed the library will automatically unbind the port. The 
-   `client_entry' is the client to which we sent the key agreement 
-   request. */
-
-void silc_client_abort_key_agreement(SilcClient client,
-                                    SilcClientConnection conn,
-                                    SilcClientEntry client_entry)
-{
-
 }
index 10fc21396b92a91c42758a51afa4f294cd4f1143..f7c6e3314b34294e0d87ba6c2356d67222f73fa0 100644 (file)
 #ifndef CLIENT_H
 #define CLIENT_H
 
-/* Forward declaration for client */
-typedef struct SilcClientObject *SilcClient;
-
-/* Forward declaration for client connection */
-typedef struct SilcClientConnectionObject *SilcClientConnection;
+/* Forward declarations */
+typedef struct SilcClientStruct *SilcClient;
+typedef struct SilcClientConnectionStruct *SilcClientConnection;
+typedef struct SilcClientPingStruct SilcClientPing;
+typedef struct SilcClientAwayStruct SilcClientAway;
+typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement;
 
 #include "idlist.h"
 #include "command.h"
 #include "silcapi.h"
 
-/* Structure to hold ping time information. Every PING command will 
-   add entry of this structure and is removed after reply to the ping
-   as been received. */
-typedef struct SilcClientPingStruct {
-  time_t start_time;
-  void *dest_id;
-  char *dest_name;
-} SilcClientPing;
-
-/* Structure to hold away messages set by user. This is mainly created
-   for future extensions where away messages could be set according filters
-   such as nickname and hostname. For now only one away message can 
-   be set in one connection. */
-typedef struct SilcClientAwayStruct {
-  char *away;
-  struct SilcClientAwayStruct *next;
-} SilcClientAway;
-
 /* Connection structure used in client to associate all the important
    connection specific data to this structure. */
-struct SilcClientConnectionObject {
+struct SilcClientConnectionStruct {
   /*
    * Local data 
    */
@@ -132,7 +115,7 @@ struct SilcClientConnectionObject {
 };
 
 /* Main client structure. */
-struct SilcClientObject {
+struct SilcClientStruct {
   /*
    * Public data. All the following pointers must be set by the allocator
    * of this structure.
@@ -240,16 +223,14 @@ void silc_client_disconnected_by_server(SilcClient client,
 void silc_client_error_by_server(SilcClient client,
                                 SilcSocketConnection sock,
                                 SilcBuffer message);
-void silc_client_notify_by_server(SilcClient client,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet);
 void silc_client_receive_new_id(SilcClient client,
                                SilcSocketConnection sock,
                                SilcIDPayload idp);
-void silc_client_new_channel_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               char *channel_name,
-                               unsigned int mode, SilcIDPayload idp);
+SilcChannelEntry silc_client_new_channel_id(SilcClient client,
+                                           SilcSocketConnection sock,
+                                           char *channel_name,
+                                           unsigned int mode, 
+                                           SilcIDPayload idp);
 void silc_client_save_channel_key(SilcClientConnection conn,
                                  SilcBuffer key_payload, 
                                  SilcChannelEntry channel);
@@ -259,9 +240,6 @@ void silc_client_receive_channel_key(SilcClient client,
 void silc_client_channel_message(SilcClient client, 
                                 SilcSocketConnection sock, 
                                 SilcPacketContext *packet);
-void silc_client_private_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet);
 void silc_client_remove_from_channels(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcClientEntry client_entry);
@@ -272,5 +250,16 @@ void silc_client_replace_from_channels(SilcClient client,
 char *silc_client_chmode(unsigned int mode);
 char *silc_client_chumode(unsigned int mode);
 char *silc_client_chumode_char(unsigned int mode);
-
+void silc_client_process_failure(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet);
+void silc_client_key_agreement(SilcClient client,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet);
+void silc_client_notify_by_server(SilcClient client,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet);
+void silc_client_private_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet);
 #endif
diff --git a/lib/silcclient/client_channel.c b/lib/silcclient/client_channel.c
new file mode 100644 (file)
index 0000000..c12532c
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+
+  client_channel.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes channel message sending and receiving routines,
+   channel key receiving and setting, and channel private key handling 
+   routines. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Sends packet to the `channel'. Packet to channel is always encrypted
+   differently from "normal" packets. SILC header of the packet is 
+   encrypted with the next receiver's key and the rest of the packet is
+   encrypted with the channel specific key. Padding and HMAC is computed
+   with the next receiver's key. The `data' is the channel message. If
+   the `force_send' is TRUE then the packet is sent immediately. */
+
+void silc_client_send_channel_message(SilcClient client, 
+                                     SilcClientConnection conn,
+                                     SilcChannelEntry channel,
+                                     unsigned char *data, 
+                                     unsigned int data_len, 
+                                     int force_send)
+{
+  int i;
+  SilcSocketConnection sock = conn->sock;
+  SilcBuffer payload;
+  SilcPacketContext packetdata;
+  SilcCipher cipher;
+  SilcHmac hmac;
+  unsigned char *id_string;
+  unsigned int iv_len;
+
+  SILC_LOG_DEBUG(("Sending packet to channel"));
+
+  if (!channel || !channel->key || !channel->hmac) {
+    client->ops->say(client, conn, 
+                    "Cannot talk to channel: key does not exist");
+    return;
+  }
+
+  /* Generate IV */
+  iv_len = silc_cipher_get_block_len(channel->channel_key);
+  if (channel->iv[0] == '\0')
+    for (i = 0; i < iv_len; i++) channel->iv[i] = 
+                                  silc_rng_get_byte(client->rng);
+  else
+    silc_hash_make(client->md5hash, channel->iv, iv_len, channel->iv);
+
+  /* Encode the channel payload. This also encrypts the message payload. */
+  payload = silc_channel_payload_encode(data_len, data, iv_len, 
+                                       channel->iv, channel->channel_key,
+                                       channel->hmac, client->rng);
+
+  /* Get data used in packet header encryption, keys and stuff. */
+  cipher = conn->send_key;
+  hmac = conn->hmac;
+  id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+
+  /* Set the packet context pointers. The destination ID is always
+     the Channel ID of the channel. Server and router will handle the
+     distribution of the packet. */
+  packetdata.flags = 0;
+  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+  packetdata.src_id = conn->local_id_data;
+  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+  packetdata.src_id_type = SILC_ID_CLIENT;
+  packetdata.dst_id = id_string;
+  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
+  packetdata.dst_id_type = SILC_ID_CHANNEL;
+  packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                                         packetdata.src_id_len +
+                                         packetdata.dst_id_len));
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          payload->len);
+
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the channel message payload to the outgoing data buffer */
+  silc_buffer_put(sock->outbuf, payload->data, payload->len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata);
+
+  /* Encrypt the header and padding of the packet. This is encrypted 
+     with normal session key shared with our server. */
+  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
+                     packetdata.src_id_len + packetdata.dst_id_len +
+                     packetdata.padlen);
+
+  SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_client_packet_send_real(client, sock, force_send);
+  silc_buffer_free(payload);
+  silc_free(id_string);
+}
+
+/* Process received message to a channel (or from a channel, really). This
+   decrypts the channel message with channel specific key and parses the
+   channel payload. Finally it displays the message on the screen. */
+
+void silc_client_channel_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcBuffer buffer = packet->buffer;
+  SilcChannelPayload payload = NULL;
+  SilcChannelID *id = NULL;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientID *client_id = NULL;
+  int found = FALSE;
+  unsigned char *message;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Sanity checks */
+  if (packet->dst_id_type != SILC_ID_CHANNEL)
+    goto out;
+
+  client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                            SILC_ID_CLIENT);
+  if (!client_id)
+    goto out;
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+  if (!id)
+    goto out;
+
+  /* Find the channel entry from channels on this connection */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+                                  SILC_ID_CHANNEL, &id_cache))
+    goto out;
+
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* Parse the channel message payload. This also decrypts the payload */
+  payload = silc_channel_payload_parse(buffer, channel->channel_key,
+                                      channel->hmac);
+  if (!payload)
+    goto out;
+
+  message = silc_channel_get_data(payload, NULL);
+
+  /* Find client entry */
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
+      found = TRUE;
+      break;
+    }
+  }
+
+  /* Pass the message to application */
+  client->ops->channel_message(client, conn, found ? chu->client : NULL,
+                              channel, message);
+
+ out:
+  if (id)
+    silc_free(id);
+  if (client_id)
+    silc_free(client_id);
+  if (payload)
+    silc_channel_payload_free(payload);
+}
+
+/* Saves channel key from encoded `key_payload'. This is used when we
+   receive Channel Key Payload and when we are processing JOIN command 
+   reply. */
+
+void silc_client_save_channel_key(SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel)
+{
+  unsigned char *id_string, *key, *cipher, hash[32];
+  unsigned int tmp_len;
+  SilcChannelID *id;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelKeyPayload payload;
+
+  payload = silc_channel_key_payload_parse(key_payload);
+  if (!payload)
+    return;
+
+  id_string = silc_channel_key_get_id(payload, &tmp_len);
+  if (!id_string) {
+    silc_channel_key_payload_free(payload);
+    return;
+  }
+
+  id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
+  if (!id) {
+    silc_channel_key_payload_free(payload);
+    return;
+  }
+
+  /* Find channel. */
+  if (!channel) {
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      goto out;
+    
+    /* Get channel entry */
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
+  /* Save the key */
+  key = silc_channel_key_get_key(payload, &tmp_len);
+  cipher = silc_channel_key_get_cipher(payload, NULL);
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
+  memcpy(channel->key, key, tmp_len);
+
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    conn->client->ops->say(conn->client, conn,
+                    "Cannot talk to channel: unsupported cipher %s", cipher);
+    goto out;
+  }
+
+  /* Set the cipher key */
+  silc_cipher_set_key(channel->channel_key, key, channel->key_len);
+
+  /* Generate HMAC key from the channel key data and set it */
+  if (!channel->hmac)
+    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+  silc_hash_make(channel->hmac->hash, key, tmp_len, hash);
+  silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+  memset(hash, 0, sizeof(hash));
+
+  /* Client is now joined to the channel */
+  channel->on_channel = TRUE;
+
+ out:
+  silc_free(id);
+  silc_channel_key_payload_free(payload);
+}
+
+/* Processes received key for channel. The received key will be used
+   to protect the traffic on the channel for now on. Client must receive
+   the key to the channel before talking on the channel is possible. 
+   This is the key that server has generated, this is not the channel
+   private key, it is entirely local setting. */
+
+void silc_client_receive_channel_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcBuffer packet)
+{
+  SILC_LOG_DEBUG(("Received key for channel"));
+
+  /* Save the key */
+  silc_client_save_channel_key(sock->user_data, packet, NULL);
+}
+
+/* Adds private key for channel. This may be set only if the channel's mode
+   mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
+   mode is not set. When channel has private key then the messages are
+   encrypted using that key. All clients on the channel must also know the
+   key in order to decrypt the messages. However, it is possible to have
+   several private keys per one channel. In this case only some of the
+   clients on the channel may now the one key and only some the other key.
+
+   The private key for channel is optional. If it is not set then the
+   channel messages are encrypted using the channel key generated by the
+   server. However, setting the private key (or keys) for the channel 
+   significantly adds security. If more than one key is set the library
+   will automatically try all keys at the message decryption phase. Note:
+   setting many keys slows down the decryption phase as all keys has to
+   be tried in order to find the correct decryption key. However, setting
+   a few keys does not have big impact to the decryption performace. 
+
+   NOTE: that this is entirely local setting. The key set using this function
+   is not sent to the network at any phase.
+
+   NOTE: If the key material was originated by the SKE protocol (using
+   silc_client_send_key_agreement) then the `key' MUST be the
+   key->send_enc_key as this is dictated by the SILC protocol. However,
+   currently it is not expected that the SKE key material would be used
+   as channel private key. However, this API allows it. */
+
+int silc_client_add_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       char *cipher,
+                                       unsigned char *key,
+                                       unsigned int key_len)
+{
+
+  return TRUE;
+}
+
+/* Removes all private keys from the `channel'. The old channel key is used
+   after calling this to protect the channel messages. Returns FALSE on
+   on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_keys(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel)
+{
+
+  return TRUE;
+}
+
+/* Removes and frees private key `key' from the channel `channel'. The `key'
+   is retrieved by calling the function silc_client_list_channel_private_keys.
+   The key is not used after this. If the key was last private key then the
+   old channel key is used hereafter to protect the channel messages. This
+   returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       SilcChannelPrivateKey key)
+{
+
+  return TRUE;
+}
+
+/* Returns array (pointers) of private keys associated to the `channel'.
+   The caller must free the array by calling the function
+   silc_client_free_channel_private_keys. The pointers in the array may be
+   used to delete the specific key by giving the pointer as argument to the
+   function silc_client_del_channel_private_key. */
+
+SilcChannelPrivateKey *
+silc_client_list_channel_private_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcChannelEntry channel,
+                                     unsigned int *key_count)
+{
+
+  return NULL;
+}
+
+/* Frees the SilcChannelPrivateKey array. */
+
+void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
+                                          unsigned int key_count)
+{
+
+}
diff --git a/lib/silcclient/client_internal.h b/lib/silcclient/client_internal.h
new file mode 100644 (file)
index 0000000..d8f049d
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+
+  client_internal.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_INTERNAL_H
+#define CLIENT_INTERNAL_H
+
+/* Internal context for connection process. This is needed as we
+   doing asynchronous connecting. */
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcTask task;
+  int sock;
+  char *host;
+  int port;
+  int tries;
+  void *context;
+} SilcClientInternalConnectContext;
+
+/* Structure to hold ping time information. Every PING command will 
+   add entry of this structure and is removed after reply to the ping
+   as been received. */
+struct SilcClientPingStruct {
+  time_t start_time;
+  void *dest_id;
+  char *dest_name;
+};
+
+/* Structure to hold away messages set by user. This is mainly created
+   for future extensions where away messages could be set according filters
+   such as nickname and hostname. For now only one away message can 
+   be set in one connection. */
+struct SilcClientAwayStruct {
+  char *away;
+  struct SilcClientAwayStruct *next;
+};
+
+/* 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 {
+  SilcClient client;
+  SilcSocketConnection sock;
+  unsigned int failure;
+} SilcClientFailureContext;
+
+/* Protypes */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
+SILC_TASK_CALLBACK_GLOBAL(silc_client_failure_callback);
+int silc_client_packet_send_real(SilcClient client,
+                                SilcSocketConnection sock,
+                                int force_send);
+
+#endif
diff --git a/lib/silcclient/client_keyagr.c b/lib/silcclient/client_keyagr.c
new file mode 100644 (file)
index 0000000..ca62805
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+
+  client_keyagr.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the Key Agreement packet processing and actual
+   key agreement routines. This file has nothing to do with the actual
+   connection key exchange protocol, it is implemented in the client.c
+   and in protocol.c. This file implements the client-to-client key 
+   agreement as defined by the SILC protocol. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_final);
+SILC_TASK_CALLBACK(silc_client_process_key_agreement);
+SILC_TASK_CALLBACK(silc_client_key_agreement_timeout);
+SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start);
+
+/* Key agreement context */
+struct SilcClientKeyAgreementStruct {
+  SilcClient client;
+  SilcClientConnection conn;
+  int fd;                              /* Listening/connection socket */
+  SilcSocketConnection sock;           /* Remote socket connection */
+  SilcClientEntry client_entry;                /* Destination client */
+  SilcKeyAgreementCallback completion; /* Key agreement completion */
+  void *context;                       /* User context */
+  SilcTask timeout;                    /* Timeout task */
+};
+
+/* Packet sending function used by the SKE in the key agreement process. */
+
+static void silc_client_key_agreement_send_packet(SilcSKE ske,
+                                                 SilcBuffer packet,
+                                                 SilcPacketType type,
+                                                 void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  void *tmp;
+
+  /* Send the packet immediately. We will assure that the packet is not
+     encrypted by setting the socket's user_data pointer to NULL. The
+     silc_client_packet_send would take the keys (wrong keys that is,
+     because user_data is the current SilcClientConnection) from it and
+     we cannot allow that. The packets are never encrypted when doing SKE
+     with another client. */
+  tmp = ske->sock->user_data;
+  ske->sock->user_data = NULL;
+  silc_client_packet_send(ctx->client, ske->sock, type, NULL, 0, NULL, NULL,
+                         packet->data, packet->len, TRUE);
+  ske->sock->user_data = tmp;
+}
+
+/* This callback is called after the key agreement protocol has been
+   performed. This calls the final completion callback for the application. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    ke->client_entry->ke = NULL;
+    ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+    silc_ske_free_key_material(ctx->keymat);
+    goto out;
+  }
+
+  /* Pass the negotiated key material to the application. The application
+     is responsible of freeing the key material. */
+  ke->client_entry->ke = NULL;
+  ke->completion(ke->client, ke->conn, ke->client_entry, ctx->keymat, 
+                ke->context);
+
+ out:
+  silc_protocol_free(protocol);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  if (ctx->dest_id)
+    silc_free(ctx->dest_id);
+  silc_task_unregister_by_callback(client->timeout_queue,
+                                  silc_client_failure_callback);
+  silc_task_unregister_by_fd(client->io_queue, ke->fd);
+  if (ke->timeout)
+    silc_task_unregister(client->timeout_queue, ke->timeout);
+  silc_socket_free(ke->sock);
+  silc_free(ke);
+  silc_free(ctx);
+}
+
+/* Key agreement callback that is called when remote end has initiated
+   the key agreement protocol. This accepts the incoming TCP/IP connection
+   for the key agreement protocol. */
+
+SILC_TASK_CALLBACK(silc_client_process_key_agreement)
+{
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+  SilcClient client = ke->client;
+  SilcClientConnection conn = ke->conn;
+  SilcSocketConnection newsocket;
+  SilcClientKEInternalContext *proto_ctx;
+  int sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  sock = silc_net_accept_connection(ke->fd);
+  if (sock < 0) {
+    client->ops->say(client, conn, 
+                    "Could not accept key agreement connection: ", 
+                    strerror(errno));
+    ke->client_entry->ke = NULL;
+    ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+    silc_task_unregister_by_fd(client->io_queue, ke->fd);
+    if (ke->timeout)
+      silc_task_unregister(client->timeout_queue, ke->timeout);
+    silc_free(ke);
+    return;
+  }
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* Create socket for this connection (it is of type UNKNOWN since this
+     really is not a real SILC connection. It is only for the key
+     agreement protocol). */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &newsocket);
+  ke->sock = newsocket;
+
+  /* Perform name and address lookups for the remote host. */
+  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
+  if (!newsocket->hostname && !newsocket->ip) {
+    client->ops->say(client, conn, 
+                    "Could not resolve the remote IP or hostname");
+    ke->client_entry->ke = NULL;
+    ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+    silc_task_unregister_by_fd(client->io_queue, ke->fd);
+    if (ke->timeout)
+      silc_task_unregister(client->timeout_queue, ke->timeout);
+    silc_free(ke);
+    return;
+  }
+  if (!newsocket->hostname)
+    newsocket->hostname = strdup(newsocket->ip);
+  newsocket->port = silc_net_get_remote_port(sock);
+
+  /* Allocate internal context for key exchange protocol. This is
+     sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = client;
+  proto_ctx->sock = newsocket;
+  proto_ctx->rng = client->rng;
+  proto_ctx->responder = TRUE;
+  proto_ctx->context = context;
+  proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+
+  /* Prepare the connection for key exchange protocol. We allocate the
+     protocol but will not start it yet. The connector will be the
+     initiator of the protocol thus we will wait for initiation from 
+     there before we start the protocol. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
+                     &newsocket->protocol, proto_ctx, 
+                     silc_client_key_agreement_final);
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it
+     will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+}
+
+/* Timeout occured during key agreement. This means that the key agreement
+   protocol was not completed in the specified timeout. We will call the 
+   completion callback. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
+{
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+
+  ke->client_entry->ke = NULL;
+  ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+
+  if (ke->sock)
+    silc_socket_free(ke->sock);
+  ke->client_entry->ke = NULL;
+  silc_free(ke);
+  silc_task_unregister_by_callback(ke->client->timeout_queue,
+                                  silc_client_failure_callback);
+  silc_task_unregister_by_fd(ke->client->io_queue, ke->fd);
+}
+
+/* Sends key agreement request to the remote client indicated by the
+   `client_entry'. If the caller provides the `hostname' and the `port'
+   arguments then the library will bind the client to that hostname and
+   that port for the key agreement protocol. It also sends the `hostname'
+   and the `port' in the key agreement packet to the remote client. This
+   would indicate that the remote client may initiate the key agreement
+   protocol to the `hostname' on the `port'.
+
+   If the `hostname' and `port' is not provided then empty key agreement
+   packet is sent to the remote client. The remote client may reply with
+   the same packet including its hostname and port. If the library receives
+   the reply from the remote client the `key_agreement' client operation
+   callback will be called to verify whether the user wants to perform the
+   key agreement or not. 
+
+   NOTE: If the application provided the `hostname' and the `port' and the 
+   remote side initiates the key agreement protocol it is not verified
+   from the user anymore whether the protocol should be executed or not.
+   By setting the `hostname' and `port' the user gives permission to
+   perform the protocol (we are responder in this case).
+
+   NOTE: If the remote side decides not to initiate the key agreement
+   or decides not to reply with the key agreement packet then we cannot
+   perform the key agreement at all. If the key agreement protocol is
+   performed the `completion' callback with the `context' will be called.
+   If remote side decides to ignore the request the `completion' will be
+   called after the specified timeout, `timeout_secs'. 
+
+   NOTE: There can be only one active key agreement for one client entry.
+   Before setting new one, the old one must be finished (it is finished
+   after calling the completion callback) or the function 
+   silc_client_abort_key_agreement must be called. */
+
+void silc_client_send_key_agreement(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcClientEntry client_entry,
+                                   char *hostname,
+                                   int port,
+                                   unsigned long timeout_secs,
+                                   SilcKeyAgreementCallback completion,
+                                   void *context)
+{
+  SilcSocketConnection sock = conn->sock;
+  SilcClientKeyAgreement ke;
+  SilcBuffer buffer;
+
+  assert(client_entry);
+
+  if (client_entry->ke)
+    return;
+
+  /* Create the listener if hostname and port was provided */
+  if (hostname && port) {
+    ke = silc_calloc(1, sizeof(*ke));
+    ke->fd = silc_net_create_server(port, hostname);
+
+    if (ke->fd < 0) {
+      client->ops->say(client, conn, 
+                      "Cannot create listener on %s on port %d: %s", 
+                      hostname, port, strerror(errno));
+      completion(client, conn, client_entry, NULL, context);
+      silc_free(ke);
+      return;
+    }
+
+    ke->client = client;
+    ke->conn = conn;
+    ke->client_entry = client_entry;
+    ke->completion = completion;
+    ke->context = context;
+
+    /* Add listener task to the queue. This task receives the key 
+       negotiations. */
+    silc_task_register(client->io_queue, ke->fd,
+                      silc_client_process_key_agreement,
+                      (void *)ke, 0, 0, 
+                      SILC_TASK_FD,
+                      SILC_TASK_PRI_NORMAL);
+
+    /* Register a timeout task that will be executed if the connector
+       will not start the key exchange protocol within the specified 
+       timeout. */
+    ke->timeout = 
+      silc_task_register(client->timeout_queue, 0, 
+                        silc_client_key_agreement_timeout,
+                        (void *)ke, timeout_secs, 0, 
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  }
+
+  /* Encode the key agreement payload */
+  buffer = silc_key_agreement_payload_encode(hostname, port);
+
+  /* Send the key agreement packet to the client */
+  silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT,
+                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                         buffer->data, buffer->len, FALSE);
+  silc_free(buffer);
+}
+
+static int 
+silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
+{
+  int sock;
+
+  /* Create connection to server asynchronously */
+  sock = silc_net_create_connection_async(ctx->port, ctx->host);
+  if (sock < 0)
+    return -1;
+
+  /* Register task that will receive the async connect and will
+     read the result. */
+  ctx->task = silc_task_register(ctx->client->io_queue, sock, 
+                                silc_client_perform_key_agreement_start,
+                                (void *)ctx, 0, 0, 
+                                SILC_TASK_FD,
+                                SILC_TASK_PRI_NORMAL);
+  silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
+  silc_schedule_set_listen_fd(sock, ctx->task->iomask);
+
+  ctx->sock = sock;
+
+  return sock;
+}
+
+/* Routine used by silc_client_perform_key_agreement to create connection
+   to the remote client on specified port. */
+
+static int
+silc_client_connect_to_client(SilcClient client, 
+                             SilcClientConnection conn, int port,
+                             char *host, void *context)
+{
+  SilcClientInternalConnectContext *ctx;
+
+  /* Allocate internal context for connection process. This is
+     needed as we are doing async connecting. */
+  ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->client = client;
+  ctx->conn = conn;
+  ctx->host = strdup(host);
+  ctx->port = port;
+  ctx->tries = 0;
+  ctx->context = context;
+
+  /* Do the actual connecting process */
+  return silc_client_connect_to_client_internal(ctx);
+}
+
+/* Callback that is called after connection has been created. This actually
+   starts the key agreement protocol. This is initiator function. */
+
+SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
+{
+  SilcClientInternalConnectContext *ctx =
+    (SilcClientInternalConnectContext *)context;
+  SilcClient client = ctx->client;
+  SilcClientConnection conn = ctx->conn;
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
+  int opt, opt_len = sizeof(opt);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Check the socket status as it might be in error */
+  getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+  if (opt != 0) {
+    if (ctx->tries < 2) {
+      /* Connection failed but lets try again */
+      client->ops->say(client, conn, "Could not connect to client %s: %s",
+                      ctx->host, strerror(opt));
+      client->ops->say(client, conn, 
+                      "Connecting to port %d of client %s resumed", 
+                      ctx->port, ctx->host);
+
+      /* Unregister old connection try */
+      silc_schedule_unset_listen_fd(fd);
+      silc_net_close_connection(fd);
+      silc_task_unregister(client->io_queue, ctx->task);
+
+      /* Try again */
+      silc_client_connect_to_client_internal(ctx);
+      ctx->tries++;
+    } else {
+      /* Connection failed and we won't try anymore */
+      client->ops->say(client, conn, "Could not connect to client %s: %s",
+                      ctx->host, strerror(opt));
+      silc_schedule_unset_listen_fd(fd);
+      silc_net_close_connection(fd);
+      silc_task_unregister(client->io_queue, ctx->task);
+      silc_free(ctx);
+
+      /* Call the completion callback */
+      ke->completion(ke->client, ke->conn, ke->client_entry, 
+                    NULL, ke->context);
+      silc_free(ke);
+    }
+    return;
+  }
+
+  silc_schedule_unset_listen_fd(fd);
+  silc_task_unregister(client->io_queue, ctx->task);
+  silc_free(ctx);
+
+  ke->fd = fd;
+
+  /* Now actually perform the key agreement protocol */
+  silc_client_perform_key_agreement_fd(ke->client, ke->conn,
+                                      ke->client_entry, ke->fd,
+                                      ke->completion, ke->context);
+  silc_free(ke);
+}
+
+/* Performs the actual key agreement protocol. Application may use this
+   to initiate the key agreement protocol. This can be called for example
+   after the application has received the `key_agreement' client operation,
+   and did not return TRUE from it.
+
+   The `hostname' is the remote hostname (or IP address) and the `port'
+   is the remote port. The `completion' callback with the `context' will
+   be called after the key agreement protocol.
+   
+   NOTE: If the application returns TRUE in the `key_agreement' client
+   operation the library will automatically start the key agreement. In this
+   case the application must not call this function. However, application
+   may choose to just ignore the `key_agreement' client operation (and
+   merely just print information about it on the screen) and call this
+   function when the user whishes to do so (by, for example, giving some
+   specific command). Thus, the API provides both, automatic and manual
+   initiation of the key agreement. Calling this function is the manual
+   initiation and returning TRUE in the `key_agreement' client operation
+   is the automatic initiation. */
+
+void silc_client_perform_key_agreement(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry client_entry,
+                                      char *hostname,
+                                      int port,
+                                      SilcKeyAgreementCallback completion,
+                                      void *context)
+{
+  SilcClientKeyAgreement ke;
+
+  assert(client_entry && hostname && port);
+
+  ke = silc_calloc(1, sizeof(*ke));
+  ke->client = client;
+  ke->conn = conn;
+  ke->client_entry = client_entry;
+  ke->completion = completion;
+  ke->context = context;
+
+  /* Connect to the remote client */
+  ke->fd = silc_client_connect_to_client(client, conn, port, hostname, ke);
+  if (ke->fd < 0) {
+    completion(client, conn, client_entry, NULL, context);
+    silc_free(ke);
+    return;
+  }
+}
+
+/* Same as above but application has created already the connection to 
+   the remote host. The `sock' is the socket to the remote connection. 
+   Application can use this function if it does not want the client library
+   to create the connection. */
+
+void silc_client_perform_key_agreement_fd(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         int sock,
+                                         SilcKeyAgreementCallback completion,
+                                         void *context)
+{
+  SilcClientKeyAgreement ke;
+  SilcClientKEInternalContext *proto_ctx;
+  SilcProtocol protocol;
+
+  assert(client_entry);
+
+  ke = silc_calloc(1, sizeof(*ke));
+  ke->client = client;
+  ke->conn = conn;
+  ke->client_entry = client_entry;
+  ke->fd = sock;
+  ke->completion = completion;
+  ke->context = context;
+
+  /* Allocate new socket connection object */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock);
+
+  /* Allocate internal context for key exchange protocol. This is
+     sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = client;
+  proto_ctx->sock = ke->sock;
+  proto_ctx->rng = client->rng;
+  proto_ctx->responder = FALSE;
+  proto_ctx->context = ke;
+  proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+
+  /* Perform key exchange protocol. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
+                     &protocol, (void *)proto_ctx,
+                     silc_client_key_agreement_final);
+  ke->sock->protocol = protocol;
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it will 
+     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)client;
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+
+  /* Execute the protocol */
+  protocol->execute(client->timeout_queue, 0, protocol, sock, 0, 0);
+}
+
+/* This function can be called to unbind the hostname and the port for
+   the key agreement protocol. However, this function has effect only 
+   before the key agreement protocol has been performed. After it has
+   been performed the library will automatically unbind the port. The 
+   `client_entry' is the client to which we sent the key agreement 
+   request. */
+
+void silc_client_abort_key_agreement(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcClientEntry client_entry)
+{
+  assert(client_entry);
+
+  if (client_entry->ke) {
+    if (client_entry->ke->sock)
+      silc_socket_free(client_entry->ke->sock);
+    client_entry->ke = NULL;
+    silc_task_unregister_by_fd(client->io_queue, client_entry->ke->fd);
+    if (client_entry->ke->timeout)
+      silc_task_unregister(client->timeout_queue, 
+                          client_entry->ke->timeout);
+    silc_free(client_entry->ke);
+  }
+}
+
+/* Callback function that is called after we've resolved the client 
+   information who sent us the key agreement packet from the server.
+   We actually call the key_agreement client operation now. */
+
+static void 
+silc_client_key_agreement_resolve_cb(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcClientEntry *clients,
+                                    unsigned int clients_count,
+                                    void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+  SilcKeyAgreementPayload payload;
+  int ret;
+  SilcKeyAgreementCallback completion;
+  void *completion_context;
+
+  if (!clients)
+    goto out;
+
+  /* Parse the key agreement payload */
+  payload = silc_key_agreement_payload_parse(packet->buffer);
+  if (!payload)
+    goto out;
+
+  /* Call the key_agreement client operation */
+  ret = client->ops->key_agreement(client, conn, clients[0], 
+                                  silc_key_agreement_get_hostname(payload),
+                                  silc_key_agreement_get_port(payload),
+                                  &completion, &completion_context);
+
+  /* If the user returned TRUE then we'll start the key agreement right
+     here and right now. */
+  if (ret == TRUE)
+    silc_client_perform_key_agreement(client, conn, clients[0],
+                                     silc_key_agreement_get_hostname(payload),
+                                     silc_key_agreement_get_port(payload),
+                                     completion, completion_context);
+
+  silc_key_agreement_payload_free(payload);
+
+ out:
+  silc_packet_context_free(packet);
+}
+
+/* Received Key Agreement packet from remote client. Process the packet
+   and resolve the client information from the server before actually
+   letting the application know that we've received this packet.  Then 
+   call the key_agreement client operation and let the user decide
+   whether we perform the key agreement protocol now or not. */
+
+void silc_client_key_agreement(SilcClient client,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
+{
+  SilcClientID *remote_id;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    return;
+
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    return;
+
+  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+                                      silc_client_key_agreement_resolve_cb,
+                                      silc_packet_context_dup(packet));
+  silc_free(remote_id);
+}
diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c
new file mode 100644 (file)
index 0000000..3d2b297
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+
+  client_notify.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the Notify packet handling. Notify packets are
+   important packets sent by the server. They tell different things to the
+   client such as nick changes, mode changes etc. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Called when notify is received and some async operation (such as command)
+   is required before processing the notify message. This calls again the
+   silc_client_notify_by_server and reprocesses the original notify packet. */
+
+static void silc_client_notify_by_server_pending(void *context)
+{
+  SilcPacketContext *p = (SilcPacketContext *)context;
+  silc_client_notify_by_server(p->context, p->sock, p);
+}
+
+/* Destructor for the pending command callback */
+
+static void silc_client_notify_by_server_destructor(void *context)
+{
+  silc_packet_context_free((SilcPacketContext *)context);
+}
+
+/* Resolve client information from server by Client ID. */
+
+static void silc_client_notify_by_server_resolve(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcPacketContext *packet,
+                                                SilcClientID *client_id)
+{
+  SilcPacketContext *p = silc_packet_context_dup(packet);
+  SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+
+  p->context = (void *)client;
+  p->sock = conn->sock;
+
+  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                             silc_client_notify_by_server_destructor,
+                             silc_client_notify_by_server_pending, p);
+  silc_buffer_free(idp);
+}
+
+/* Received notify message from server */
+
+void silc_client_notify_by_server(SilcClient client,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcNotifyPayload payload;
+  SilcNotifyType type;
+  SilcArgumentPayload args;
+
+  SilcClientID *client_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcClientEntry client_entry;
+  SilcClientEntry client_entry2;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  SilcIDCacheEntry id_cache = NULL;
+  unsigned char *tmp;
+  unsigned int tmp_len, mode;
+
+  payload = silc_notify_payload_parse(buffer);
+  if (!payload)
+    goto out;
+
+  type = silc_notify_get_type(payload);
+  args = silc_notify_get_args(payload);
+  if (!args)
+    goto out;
+
+  switch(type) {
+  case SILC_NOTIFY_TYPE_NONE:
+    /* Notify application */
+    client->ops->notify(client, conn, type, 
+                       silc_argument_get_arg_type(args, 1, NULL));
+    break;
+
+  case SILC_NOTIFY_TYPE_INVITE:
+    /* 
+     * Someone invited me to a channel. Find Client and Channel entries
+     * for the application.
+     */
+    
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry and if not found query it */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* XXX Will ALWAYS fail because currently we don't have way to resolve
+       channel information for channel that we're not joined to. */
+    /* XXX ways to fix: use (extended) LIST command, or define the channel
+       name to the notfy type when name resolving is not mandatory. */
+    /* Find channel entry */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      goto out;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_JOIN:
+    /*
+     * Someone has joined to a channel. Get their ID and nickname and
+     * cache them for later use.
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry and if not found query it */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* If nickname or username hasn't been resolved, do so */
+    if (!client_entry->nickname || !client_entry->username) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Add client to channel */
+    chu = silc_calloc(1, sizeof(*chu));
+    chu->client = client_entry;
+    silc_list_add(channel->clients, chu);
+
+    /* XXX add support for multiple same nicks on same channel. Check
+       for them here */
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_LEAVE:
+    /*
+     * Someone has left a channel. We will remove it from the channel but
+     * we'll keep it in the cache in case we'll need it later.
+     */
+    
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Remove client from channel */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       silc_list_del(channel->clients, chu);
+       silc_free(chu);
+       break;
+      }
+    }
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    /*
+     * Someone left SILC. We'll remove it from all channels and from cache.
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Remove from all channels */
+    silc_client_remove_from_channels(client, conn, client_entry);
+
+    /* Remove from cache */
+    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
+                          client_entry->id);
+
+    /* Get signoff message */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp_len > 128)
+      tmp = NULL;
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, client_entry, tmp);
+
+    /* Free data */
+    if (client_entry->nickname)
+      silc_free(client_entry->nickname);
+    if (client_entry->server)
+      silc_free(client_entry->server);
+    if (client_entry->id)
+      silc_free(client_entry->id);
+    if (client_entry->send_key)
+      silc_cipher_free(client_entry->send_key);
+    if (client_entry->receive_key)
+      silc_cipher_free(client_entry->receive_key);
+    break;
+
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    /*
+     * Someone set the topic on a channel.
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get topic */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, tmp, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+    /*
+     * Someone changed their nickname. If we don't have entry for the new
+     * ID we will query it and return here after it's done. After we've
+     * returned we fetch the old entry and free it and notify the 
+     * application.
+     */
+
+    /* Get new Client ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Ignore my ID */
+    if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
+      break;
+
+    /* Find Client entry and if not found query it */
+    client_entry2 = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry2) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+    silc_free(client_id);
+
+    /* Get old Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find old Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Remove the old from cache */
+    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
+                          client_entry->id);
+
+    /* Replace old ID entry with new one on all channels. */
+    silc_client_replace_from_channels(client, conn, client_entry,
+                                     client_entry2);
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, client_entry, client_entry2);
+
+    /* Free data */
+    if (client_entry->nickname)
+      silc_free(client_entry->nickname);
+    if (client_entry->server)
+      silc_free(client_entry->server);
+    if (client_entry->id)
+      silc_free(client_entry->id);
+    if (client_entry->send_key)
+      silc_cipher_free(client_entry->send_key);
+    if (client_entry->receive_key)
+      silc_cipher_free(client_entry->receive_key);
+    break;
+
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    /*
+     * Someone changed a channel mode
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    SILC_GET32_MSB(mode, tmp);
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Save the new mode */
+    channel->mode = mode;
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, mode, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    /*
+     * Someone changed user's mode on a channel
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    SILC_GET32_MSB(mode, tmp);
+
+    /* Get target Client ID */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    silc_free(client_id);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find target Client entry */
+    client_entry2 = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry2)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Save the mode */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       chu->mode = mode;
+       break;
+      }
+    }
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, mode, 
+                       client_entry2, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_MOTD:
+    /*
+     * Received Message of the day
+     */
+
+    /* Get motd */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    
+    /* Notify application */
+    client->ops->notify(client, conn, type, tmp);
+    break;
+
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    /*
+     * Router has enforced a new ID to a channel. Let's change the old
+     * ID to the one provided here.
+     */
+
+    /* Get the old ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+    
+    /* Get the channel entry */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Free the old ID */
+    silc_free(channel_id);
+    silc_free(channel->id);
+
+    /* Get the new ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel->id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel->id)
+      goto out;
+
+    id_cache->id = (void *)channel->id;
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, channel, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_KICKED:
+    /*
+     * A client (maybe me) was kicked from a channel
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Get comment */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, tmp, channel);
+
+    /* If I was kicked from channel, remove the channel */
+    if (client_entry == conn->local_entry) {
+      if (conn->current_channel == channel)
+       conn->current_channel = NULL;
+      silc_idcache_del_by_id(conn->channel_cache, 
+                            SILC_ID_CHANNEL, channel->id);
+      silc_free(channel->channel_name);
+      silc_free(channel->id);
+      silc_free(channel->key);
+      silc_cipher_free(channel->channel_key);
+      silc_free(channel);
+    }
+    break;
+    
+  default:
+    break;
+  }
+
+ out:
+  silc_notify_payload_free(payload);
+  if (client_id)
+    silc_free(client_id);
+  if (channel_id)
+    silc_free(channel_id);
+}
diff --git a/lib/silcclient/client_prvmsg.c b/lib/silcclient/client_prvmsg.c
new file mode 100644 (file)
index 0000000..6164b70
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+
+  client_prvmsg.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the private message sending and receiving routines
+   and private message key handling routines. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Sends private message to remote client. If private message key has
+   not been set with this client then the message will be encrypted using
+   normal session keys. Private messages are special packets in SILC
+   network hence we need this own function for them. This is similiar
+   to silc_client_packet_send_to_channel except that we send private
+   message. The `data' is the private message. If the `force_send' is
+   TRUE the packet is sent immediately. */
+
+void silc_client_send_private_message(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry,
+                                     unsigned char *data, 
+                                     unsigned int data_len, 
+                                     int force_send)
+{
+  SilcSocketConnection sock = conn->sock;
+  SilcBuffer buffer;
+  SilcPacketContext packetdata;
+  unsigned int nick_len;
+  SilcCipher cipher;
+  SilcHmac hmac;
+
+  SILC_LOG_DEBUG(("Sending private message"));
+
+  /* Create private message payload */
+  nick_len = strlen(conn->nickname);
+  buffer = silc_buffer_alloc(2 + nick_len + data_len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(nick_len),
+                    SILC_STR_UI_XNSTRING(conn->nickname,
+                                         nick_len),
+                    SILC_STR_UI_XNSTRING(data, data_len),
+                    SILC_STR_END);
+
+  /* If we don't have private message specific key then private messages
+     are just as any normal packet thus call normal packet sending.  If
+     the key exist then the encryption process is a bit different and
+     will be done in the rest of this function. */
+  if (!client_entry->send_key) {
+    silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
+                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                           buffer->data, buffer->len, force_send);
+    goto out;
+  }
+
+  /* We have private message specific key */
+
+  /* Get data used in the encryption */
+  cipher = client_entry->send_key;
+  hmac = conn->hmac;
+
+  /* Set the packet context pointers. */
+  packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
+  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
+  packetdata.src_id = conn->local_id_data;
+  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+  packetdata.src_id_type = SILC_ID_CLIENT;
+  packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
+  packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
+  packetdata.dst_id_type = SILC_ID_CLIENT;
+  packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                                         packetdata.src_id_len +
+                                         packetdata.dst_id_len));
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          buffer->len);
+  
+  packetdata.buffer = sock->outbuf;
+
+  /* Encrypt payload of the packet. Encrypt with private message specific
+     key */
+  cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
+                         buffer->len, cipher->iv);
+      
+  /* Put the actual encrypted payload data into the buffer. */
+  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata);
+
+  /* Encrypt the header and padding of the packet. */
+  cipher = conn->send_key;
+  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
+                     packetdata.src_id_len + packetdata.dst_id_len +
+                     packetdata.padlen);
+
+  SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_client_packet_send_real(client, sock, force_send);
+  silc_free(packetdata.dst_id);
+
+ out:
+  silc_free(buffer);
+}     
+
+/* Private message received. This processes the private message and
+   finally displays it on the screen. */
+
+void silc_client_private_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcBuffer buffer = packet->buffer;
+  SilcIDCacheEntry id_cache;
+  SilcClientID *remote_id = NULL;
+  SilcClientEntry remote_client;
+  unsigned short nick_len;
+  unsigned char *nickname, *message = NULL;
+  int ret;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    goto out;
+
+  /* Get nickname */
+  ret = silc_buffer_unformat(buffer, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
+                            SILC_STR_END);
+  if (ret == -1)
+    return;
+
+  silc_buffer_pull(buffer, 2 + nick_len);
+
+  message = silc_calloc(buffer->len + 1, sizeof(char));
+  memcpy(message, buffer->data, buffer->len);
+
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    goto out;
+
+  /* Check whether we know this client already */
+  if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
+                                  SILC_ID_CLIENT, &id_cache))
+    {
+      /* Allocate client entry */
+      remote_client = silc_calloc(1, sizeof(*remote_client));
+      remote_client->id = remote_id;
+      silc_parse_nickname(nickname, &remote_client->nickname, 
+                         &remote_client->server, &remote_client->num);
+      
+      /* Save the client to cache */
+      silc_idcache_add(conn->client_cache, remote_client->nickname,
+                      SILC_ID_CLIENT, remote_client->id, remote_client, 
+                      TRUE);
+    } else {
+      remote_client = (SilcClientEntry)id_cache->context;
+    }
+
+  /* Pass the private message to application */
+  client->ops->private_message(client, conn, remote_client, message);
+
+  /* See if we are away (gone). If we are away we will reply to the
+     sender with the set away message. */
+  if (conn->away && conn->away->away) {
+    /* If it's me, ignore */
+    if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
+      goto out;
+
+    /* Send the away message */
+    silc_client_send_private_message(client, conn, remote_client,
+                                    conn->away->away,
+                                    strlen(conn->away->away), TRUE);
+  }
+
+ out:
+  if (remote_id)
+    silc_free(remote_id);
+
+  if (message) {
+    memset(message, 0, buffer->len);
+    silc_free(message);
+  }
+  silc_free(nickname);
+}
+
+/* Function that actually employes the received private message key */
+
+static void silc_client_private_message_key_cb(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcClientEntry *clients,
+                                              unsigned int clients_count,
+                                              void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+  unsigned char *key;
+  unsigned short key_len;
+  unsigned char *cipher;
+  int ret;
+
+  if (!clients)
+    goto out;
+
+  /* Parse the private message key payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI16_NSTRING(&key, &key_len),
+                            SILC_STR_UI16_STRING(&cipher),
+                            SILC_STR_END);
+  if (!ret)
+    goto out;
+
+  if (key_len > packet->buffer->len)
+    goto out;
+
+  /* Now take the key in use */
+  if (!silc_client_add_private_message_key(client, conn, clients[0],
+                                          cipher, key, key_len, FALSE))
+    goto out;
+
+  /* Print some info for application */
+  client->ops->say(client, conn, 
+                  "Received private message key from %s%s%s %s%s%s", 
+                  clients[0]->nickname,
+                  clients[0]->server ? "@" : "",
+                  clients[0]->server ? clients[0]->server : "",
+                  clients[0]->username ? "(" : "",
+                  clients[0]->username ? clients[0]->username : "",
+                  clients[0]->username ? ")" : "");
+
+ out:
+  silc_packet_context_free(packet);
+}
+
+/* Processes incoming Private Message Key payload. The libary always
+   accepts the key and takes it into use. */
+
+void silc_client_private_message_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
+{
+  SilcClientID *remote_id;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    return;
+
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    return;
+
+  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+                                      silc_client_private_message_key_cb,
+                                      silc_packet_context_dup(packet));
+  silc_free(remote_id);
+}
+
+/* Adds private message key to the client library. The key will be used to
+   encrypt all private message between the client and the remote client
+   indicated by the `client_entry'. If the `key' is NULL and the boolean
+   value `generate_key' is TRUE the library will generate random key.
+   The `key' maybe for example pre-shared-key, passphrase or similar.
+   The `cipher' MAY be provided but SHOULD be NULL to assure that the
+   requirements of the SILC protocol are met. The API, however, allows
+   to allocate any cipher.
+
+   It is not necessary to set key for normal private message usage. If the
+   key is not set then the private messages are encrypted using normal
+   session keys. Setting the private key, however, increases the security. 
+
+   Returns FALSE if the key is already set for the `client_entry', TRUE
+   otherwise. */
+
+int silc_client_add_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry,
+                                       char *cipher,
+                                       unsigned char *key,
+                                       unsigned int key_len,
+                                       int generate_key)
+{
+  unsigned char private_key[32];
+  unsigned int len;
+  int i;
+  SilcSKEKeyMaterial *keymat;
+
+  assert(client_entry);
+
+  /* Return FALSE if key already set */
+  if (client_entry->send_key && client_entry->receive_key)
+    return FALSE;
+
+  if (!cipher)
+    cipher = "aes-256-cbc";
+
+  /* Check the requested cipher */
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  /* Generate key if not provided */
+  if (!key && generate_key == TRUE) {
+    len = 32;
+    for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
+    key = private_key;
+    key_len = len;
+    client_entry->generated = TRUE;
+  }
+
+  /* Save the key */
+  client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
+  memcpy(client_entry->key, key, key_len);
+  client_entry->key_len = key_len;
+
+  /* Produce the key material as the protocol defines */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
+                                        client->md5hash, keymat) 
+      != SILC_SKE_STATUS_OK)
+    return FALSE;
+
+  /* Allocate the ciphers */
+  silc_cipher_alloc(cipher, &client_entry->send_key);
+  silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+  /* Set the keys */
+  silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
+                     keymat->enc_key_len);
+  silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
+  silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
+                     keymat->enc_key_len);
+  silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
+
+  /* Free the key material */
+  silc_ske_free_key_material(keymat);
+
+  return TRUE;
+}
+
+/* Same as above but takes the key material from the SKE key material
+   structure. This structure is received if the application uses the
+   silc_client_send_key_agreement to negotiate the key material. The
+   `cipher' SHOULD be provided as it is negotiated also in the SKE
+   protocol. */
+
+int silc_client_add_private_message_key_ske(SilcClient client,
+                                           SilcClientConnection conn,
+                                           SilcClientEntry client_entry,
+                                           char *cipher,
+                                           SilcSKEKeyMaterial *key)
+{
+  assert(client_entry);
+
+  /* Return FALSE if key already set */
+  if (client_entry->send_key && client_entry->receive_key)
+    return FALSE;
+
+  if (!cipher)
+    cipher = "aes-256-cbc";
+
+  /* Check the requested cipher */
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  /* Allocate the ciphers */
+  silc_cipher_alloc(cipher, &client_entry->send_key);
+  silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+  /* Set the keys */
+  silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
+                     key->enc_key_len);
+  silc_cipher_set_iv(client_entry->send_key, key->send_iv);
+  silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
+                     key->enc_key_len);
+  silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+
+  return TRUE;
+}
+
+/* Sends private message key payload to the remote client indicated by
+   the `client_entry'. If the `force_send' is TRUE the packet is sent
+   immediately. Returns FALSE if error occurs, TRUE otherwise. The
+   application should call this function after setting the key to the
+   client.
+
+   Note that the key sent using this function is sent to the remote client
+   through the SILC network. The packet is protected using normal session
+   keys. */
+
+int silc_client_send_private_message_key(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        int force_send)
+{
+  SilcSocketConnection sock = conn->sock;
+  SilcBuffer buffer;
+  int cipher_len;
+
+  if (!client_entry->send_key || !client_entry->key)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Sending private message key"));
+
+  cipher_len = strlen(client_entry->send_key->cipher->name);
+
+  /* Create private message key payload */
+  buffer = silc_buffer_alloc(2 + client_entry->key_len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(client_entry->key_len),
+                    SILC_STR_UI_XNSTRING(client_entry->key, 
+                                         client_entry->key_len),
+                    SILC_STR_UI_SHORT(cipher_len),
+                    SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
+                                         cipher_len),
+                    SILC_STR_END);
+
+  /* Send the packet */
+  silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                         buffer->data, buffer->len, force_send);
+  silc_free(buffer);
+
+  return TRUE;
+}
+
+/* Removes the private message from the library. The key won't be used
+   after this to protect the private messages with the remote `client_entry'
+   client. Returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry)
+{
+  assert(client_entry);
+
+  if (!client_entry->send_key && !client_entry->receive_key)
+    return FALSE;
+
+  silc_cipher_free(client_entry->send_key);
+  silc_cipher_free(client_entry->receive_key);
+
+  if (client_entry->key) {
+    memset(client_entry->key, 0, client_entry->key_len);
+    silc_free(client_entry->key);
+  }
+
+  client_entry->send_key = NULL;
+  client_entry->receive_key = NULL;
+  client_entry->key = NULL;
+
+  return TRUE;
+}
+
+/* Returns array of set private message keys associated to the connection
+   `conn'. Returns allocated SilcPrivateMessageKeys array and the array
+   count to the `key_count' argument. The array must be freed by the caller
+   by calling the silc_client_free_private_message_keys function. Note: 
+   the keys returned in the array is in raw format. It might not be desired
+   to show the keys as is. The application might choose not to show the keys
+   at all or to show the fingerprints of the keys. */
+
+SilcPrivateMessageKeys
+silc_client_list_private_message_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     unsigned int *key_count)
+{
+  SilcPrivateMessageKeys keys;
+  unsigned int count = 0;
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcClientEntry entry;
+
+  if (!silc_idcache_find_by_id(conn->client_cache, SILC_ID_CACHE_ANY, 
+                              SILC_ID_CLIENT, &list))
+    return NULL;
+
+  if (!silc_idcache_list_count(list)) {
+    silc_idcache_list_free(list);
+    return NULL;
+  }
+
+  keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
+
+  silc_idcache_list_first(list, &id_cache);
+  while (id_cache) {
+    entry = (SilcClientEntry)id_cache->context;
+
+    if (entry->send_key) {
+      keys[count].client_entry = entry;
+      keys[count].cipher = entry->send_key->cipher->name;
+      keys[count].key = entry->generated == FALSE ? entry->key : NULL;
+      keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
+      count++;
+    }
+
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+  }
+
+  if (key_count)
+    *key_count = count;
+
+  return keys;
+}
+
+/* Frees the SilcPrivateMessageKeys array returned by the function
+   silc_client_list_private_message_keys. */
+
+void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
+                                          unsigned int key_count)
+{
+  silc_free(keys);
+}
index 733072c5ee2a01693086e64fbebcaf97ab2d2abd..7e450da9627a07599c0e62a663d8f8bb56c913a1 100644 (file)
@@ -20,6 +20,7 @@
 /* $Id$ */
 
 #include "clientlibincludes.h"
+#include "client_internal.h"
 
 /* Client command list. */
 SilcClientCommand silc_command_list[] =
@@ -41,7 +42,7 @@ SilcClientCommand silc_command_list[] =
   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
   SILC_CLIENT_CMD(oper, OPER, "OPER",
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 4),
+  SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
index b1187bb9fe89f0359988906a59abe991d591089c..4f9666172e753c96e1c0ef125cb358c29b467f4e 100644 (file)
@@ -34,6 +34,7 @@
 /* $Id$ */
 
 #include "clientlibincludes.h"
+#include "client_internal.h"
 
 /* Client command reply list. */
 SilcClientCommandReply silc_command_reply_list[] =
@@ -743,8 +744,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   SilcClient client = cmd->client;
   SilcCommandStatus status;
   SilcIDPayload idp = NULL;
+  SilcChannelEntry channel;
   unsigned int argc, mode, len;
-  char *topic, *tmp, *channel_name = NULL;
+  char *topic, *tmp, *channel_name = NULL, *hmac;
   SilcBuffer keyp;
 
   SILC_LOG_DEBUG(("Start"));
@@ -810,15 +812,28 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   silc_buffer_put(keyp, tmp, len);
 
   /* Get topic */
-  topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
+  topic = silc_argument_get_arg_type(cmd->args, 9, NULL);
 
-  /* Save received Channel ID */
-  silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
-                            mode, idp);
+  /* Save received Channel ID. This actually creates the channel */
+  channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
+                                      mode, idp);
   silc_id_payload_free(idp);
 
+  /* Get hmac */
+  hmac = silc_argument_get_arg_type(cmd->args, 10, NULL);
+  if (hmac) {
+    if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+      cmd->client->ops->say(cmd->client, conn, 
+                           "Cannot join channel: Unsupported HMAC `%s'",
+                           hmac);
+      COMMAND_REPLY_ERROR;
+      silc_free(channel_name);
+      goto out;
+    }
+  }
+
   /* Save channel key */
-  silc_client_save_channel_key(conn, keyp, conn->current_channel);
+  silc_client_save_channel_key(conn, keyp, channel);
   silc_buffer_free(keyp);
 
   if (topic)
@@ -826,8 +841,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
                     "Topic for %s: %s", channel_name, topic);
 
   /* Notify application */
-  COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
-                NULL, NULL, topic));
+  COMMAND_REPLY((ARGS, channel_name, channel, mode, NULL, NULL, topic, hmac));
 
   /* Execute any pending command callbacks */
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
index 1197a90bf47e636f4290a53f19efde326292279a..7a2fc657070290709f7ef62d23f017830bdb9901 100644 (file)
@@ -325,7 +325,7 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
 
   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-  silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY
+  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS
                           ++conn->cmd_ident,
                           1, 3, idp->data, idp->len);
   silc_buffer_free(idp);
@@ -337,7 +337,7 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
   i->context = context;
       
   /* Add pending callback */
-  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS
                              ++conn->cmd_ident, 
                              silc_client_get_client_by_id_destructor,
                              silc_client_command_get_client_by_id_callback, 
index ecf8b9b44e8d94ba51d803457c2e5910dd58fd27..f7d693370b0d4a8c87c02719c3d9045bfc65304b 100644 (file)
@@ -39,6 +39,7 @@ typedef struct SilcClientEntryStruct {
                                   generated the key. */
   unsigned int key_len;
   int generated;               /* TRUE if library generated the key */
+  SilcClientKeyAgreement ke;   /* Current key agreement context or NULL */
 } *SilcClientEntry;
 
 /* Client and its mode on a channel */
@@ -66,6 +67,7 @@ typedef struct SilcChannelEntryStruct {
   unsigned char *key;
   unsigned int key_len;
   unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+  SilcHmac hmac;
 } *SilcChannelEntry;
 
 /* Prototypes (some functions are defined in the silcapi.h) */
index d0734863b377f9bc9df1208c1d5c7016e27fce5b..a408802684560e4685614280028bee2eafd8bbee 100644 (file)
@@ -23,6 +23,7 @@
 /* $Id$ */
 
 #include "clientlibincludes.h"
+#include "client_internal.h"
 
 SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
 SILC_TASK_CALLBACK(silc_client_protocol_key_exchange);
@@ -35,10 +36,10 @@ extern char *silc_version_string;
 
 /* Function that is called when SKE protocol sends packets to network. */
 
-static void silc_client_protocol_ke_send_packet(SilcSKE ske,
-                                               SilcBuffer packet,
-                                               SilcPacketType type,
-                                               void *context)
+void silc_client_protocol_ke_send_packet(SilcSKE ske,
+                                        SilcBuffer packet,
+                                        SilcPacketType type,
+                                        void *context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
   SilcClientKEInternalContext *ctx = 
@@ -78,15 +79,15 @@ silc_client_protocol_ke_verify_key(SilcSKE ske,
 
 /* Sets the negotiated key material into use for particular connection. */
 
-static void silc_client_protocol_ke_set_keys(SilcSKE ske,
-                                            SilcSocketConnection sock,
-                                            SilcSKEKeyMaterial *keymat,
-                                            SilcCipher cipher,
-                                            SilcPKCS pkcs,
-                                            SilcHash hash)
+void silc_client_protocol_ke_set_keys(SilcSKE ske,
+                                     SilcSocketConnection sock,
+                                     SilcSKEKeyMaterial *keymat,
+                                     SilcCipher cipher,
+                                     SilcPKCS pkcs,
+                                     SilcHash hash,
+                                     SilcHmac hmac)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcHash nhash;
 
   SILC_LOG_DEBUG(("Setting new keys into use"));
 
@@ -114,8 +115,7 @@ static void silc_client_protocol_ke_set_keys(SilcSKE ske,
 #endif
 
   /* Save HMAC key to be used in the communication. */
-  silc_hash_alloc(hash->hash->name, &nhash);
-  silc_hmac_alloc(nhash, &conn->hmac);
+  silc_hmac_alloc(hmac->hmac->name, NULL, &conn->hmac);
   silc_hmac_set_key(conn->hmac, keymat->hmac_key, keymat->hmac_key_len);
 }
 
@@ -179,17 +179,11 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       ske->user_data = (void *)client;
       
       if (ctx->responder == TRUE) {
-#if 0
-       SilcBuffer start_payload;
-
-
        /* Start the key exchange by processing the received security
           properties packet from initiator. */
        status = silc_ske_responder_start(ske, ctx->rng, ctx->sock,
-                                         start_payload,
-                                         silc_client_protocol_ke_send_packet,
-                                         context);
-#endif
+                                         silc_version_string,
+                                         ctx->packet->buffer, NULL, NULL);
       } else {
        SilcSKEStartPayload *start_payload;
 
@@ -202,7 +196,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
           to the remote end. */
        status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock,
                                          start_payload,
-                                         silc_client_protocol_ke_send_packet,
+                                         ctx->send_packet,
                                          context);
       }
 
@@ -217,8 +211,10 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        return;
       }
 
-      /* Advance the state of the protocol. */
+      /* Advance protocol state and call the next state if we are responder */
       protocol->state++;
+      if (ctx->responder == TRUE)
+       protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 100000);
     }
     break;
   case 2:
@@ -227,13 +223,12 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        * Phase 1 
        */
       if (ctx->responder == TRUE) {
-#if 0
+       /* Sends the selected security properties to the initiator. */
        status = 
          silc_ske_responder_phase_1(ctx->ske, 
                                     ctx->ske->start_payload,
-                                    silc_server_protocol_ke_send_packet,
+                                    ctx->send_packet,
                                     context);
-#endif
       } else {
        /* Call Phase-1 function. This processes the Key Exchange Start
           paylaod reply we just got from the responder. The callback
@@ -254,9 +249,10 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        return;
       }
 
-      /* Advance the state of the protocol and call the next state. */
+      /* Advance protocol state and call next state if we are initiator */
       protocol->state++;
-      protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+      if (ctx->responder == FALSE)
+       protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 100000);
     }
     break;
   case 3:
@@ -265,13 +261,11 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        * Phase 2 
        */
       if (ctx->responder == TRUE) {
-#if 0
-       status = 
-         silc_ske_responder_phase_2(ctx->ske, 
-                                    ctx->ske->start_payload,
-                                    silc_server_protocol_ke_send_packet,
-                                    context);
-#endif
+       /* Process the received Key Exchange 1 Payload packet from
+          the initiator. This also creates our parts of the Diffie
+          Hellman algorithm. */
+       status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer, 
+                                           NULL, NULL);
       } else {
        /* Call the Phase-2 function. This creates Diffie Hellman
           key exchange parameters and sends our public part inside
@@ -279,7 +273,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        status = 
          silc_ske_initiator_phase_2(ctx->ske,
                                     client->public_key,
-                                    silc_client_protocol_ke_send_packet,
+                                    ctx->send_packet,
                                     context);
       }
 
@@ -294,8 +288,10 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        return;
       }
 
-      /* Advance the state of the protocol. */
+      /* Advance protocol state and call the next state if we are responder */
       protocol->state++;
+      if (ctx->responder == TRUE)
+       protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 100000);
     }
     break;
   case 4:
@@ -304,14 +300,15 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        * Finish protocol
        */
       if (ctx->responder == TRUE) {
-       status = 0;
-#if 0
+       /* This creates the key exchange material and sends our
+          public parts to the initiator inside Key Exchange 2 Payload. */
        status = 
-         silc_ske_responder_phase_2(ctx->ske, 
-                                    ctx->ske->start_payload,
-                                    silc_server_protocol_ke_send_packet,
-                                    context);
-#endif
+         silc_ske_responder_finish(ctx->ske, 
+                                   client->public_key, client->private_key,
+                                   SILC_SKE_PK_TYPE_SILC,
+                                   ctx->send_packet,
+                                   context);
+       status = 0;
       } else {
        /* Finish the protocol. This verifies the Key Exchange 2 payload
           sent by responder. */
@@ -338,7 +335,8 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       
       /* Send Ok to the other end. We will end the protocol as server
         sends Ok to us when we will take the new keys into use. */
-      silc_ske_end(ctx->ske, silc_client_protocol_ke_send_packet, context);
+      if (ctx->responder == FALSE)
+       silc_ske_end(ctx->ske, ctx->send_packet, context);
       
       /* End the protocol on the next round */
       protocol->state = SILC_PROTOCOL_STATE_END;
@@ -351,21 +349,31 @@ SILC_TASK_CALLBACK(silc_client_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;
 
       /* Process the key material */
       keymat = silc_calloc(1, sizeof(*keymat));
-      silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len, 
-                                   keymat);
+      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(client->timeout_queue, 0, protocol, fd, 0, 300000);
+       silc_ske_free_key_material(keymat);
+       return;
+      }
+      ctx->keymat = keymat;
 
-      /* Take the negotiated keys into use. */
-      silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat,
-                                      ctx->ske->prop->cipher,
-                                      ctx->ske->prop->pkcs,
-                                      ctx->ske->prop->hash);
+      /* 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, ctx->send_packet, context);
 
-      silc_ske_free_key_material(keymat);
+      /* Unregister the timeout task since the protocol has ended. 
+        This was the timeout task to be executed if the protocol is
+        not completed fast enough. */
+      if (ctx->timeout_task)
+       silc_task_unregister(client->timeout_queue, ctx->timeout_task);
 
       /* Protocol has ended, call the final callback */
       if (protocol->final_callback)
@@ -382,8 +390,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
     
     /* Send abort notification */
     silc_ske_abort(ctx->ske, ctx->ske->status, 
-                  silc_client_protocol_ke_send_packet,
-                  context);
+                  ctx->send_packet, context);
 
     /* On error the final callback is always called. */
     if (protocol->final_callback)
@@ -397,6 +404,12 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
      * Received failure from remote.
      */
 
+    /* Unregister the timeout task since the protocol has ended. 
+       This was the timeout task to be executed if the protocol is
+       not completed fast enough. */
+    if (ctx->timeout_task)
+      silc_task_unregister(client->timeout_queue, ctx->timeout_task);
+
     /* On error the final callback is always called. */
     if (protocol->final_callback)
       protocol->execute_final(client->timeout_queue, 0, protocol, fd);
index 928479023a4bcd93e5dca733cbfec44f3ea4e10f..92c195abb6615e6dcf91cd0d790db0ed871d75d8 100644 (file)
@@ -34,13 +34,16 @@ typedef struct {
   SilcRng rng;
   int responder;
 
-  /* Destinations ID taken from authenticataed packet so that we can
-     get the destinations ID. */
-  void *dest_id;
-  SilcIdType dest_id_type;
+  void *dest_id;                   /* Destination ID from packet */
+  SilcIdType dest_id_type;         /* Destination ID type */
 
+  SilcTask timeout_task;
   SilcPacketContext *packet;
-  SilcSKE ske;
+
+  SilcSKESendPacketCb send_packet;  /* SKE's packet sending callback */
+  SilcSKE ske;                     /* The SKE object */
+  SilcSKEKeyMaterial *keymat;      /* The negotiated key material */
+  void *context;                   /* Internal context */
 } SilcClientKEInternalContext;
 
 /* Internal context for connection authentication protocol */
@@ -71,5 +74,16 @@ typedef struct {
 /* Prototypes */
 void silc_client_protocols_register(void);
 void silc_client_protocols_unregister(void);
+void silc_client_protocol_ke_send_packet(SilcSKE ske,
+                                        SilcBuffer packet,
+                                        SilcPacketType type,
+                                        void *context);
+void silc_client_protocol_ke_set_keys(SilcSKE ske,
+                                     SilcSocketConnection sock,
+                                     SilcSKEKeyMaterial *keymat,
+                                     SilcCipher cipher,
+                                     SilcPKCS pkcs,
+                                     SilcHash hash,
+                                     SilcHmac hmac);
 
 #endif
index 658fc5f7adfb8504ddd9874634340ee1654c68cf..a546b9573362b30d9e059a021987b0fa0781a8d9 100644 (file)
    of how to use the SILC Client Library.
 */
 
+/* Key agreement callback that is called after the key agreement protocol
+   has been performed. This is called also if error occured during the
+   key agreement protocol. The `key' is the allocated key material and
+   the caller is responsible of freeing it. The `key' is NULL if error
+   has occured. The application can freely use the `key' to whatever
+   purpose it needs. See lib/silcske/silcske.h for the definition of
+   the SilcSKEKeyMaterial structure. */
+typedef void (*SilcKeyAgreementCallback)(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        SilcSKEKeyMaterial *key,
+                                        void *context);
+
 /******************************************************************************
 
                            SILC Client Operations
@@ -154,10 +167,13 @@ typedef struct {
      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). */
+     silc_client_perform_key_agreement). If TRUE is returned also the
+     `completion' and `context' arguments must be set by the application. */
   int (*key_agreement)(SilcClient client, SilcClientConnection conn,
                       SilcClientEntry client_entry, char *hostname,
-                      int port);
+                      int port,
+                      SilcKeyAgreementCallback *completion,
+                      void **context);
 } SilcClientOperations;
 
 
@@ -172,7 +188,7 @@ typedef struct {
 
 ******************************************************************************/
 
-/* Initialization functions */
+/* Initialization functions (client.c) */
 
 /* Allocates new client object. This has to be done before client may
    work. After calling this one must call silc_client_init to initialize
@@ -197,7 +213,7 @@ void silc_client_run(SilcClient client);
 void silc_client_stop(SilcClient client);
 
 
-/* Connecting functions */
+/* Connecting functions (client.c) */
 
 /* Connects to remote server. This is the main routine used to connect
    to SILC server. Returns -1 on error and the created socket otherwise. 
@@ -240,7 +256,7 @@ void silc_client_close_connection(SilcClient client,
                                  SilcClientConnection conn);
 
 
-/* Message sending functions */
+/* Message sending functions (client_channel.c and client_prvmsg.c) */
 
 /* Sends packet to the `channel'. Packet to channel is always encrypted
    differently from "normal" packets. SILC header of the packet is 
@@ -270,7 +286,7 @@ void silc_client_send_private_message(SilcClient client,
                                      int force_send);
 
 
-/* Client and Channel entry retrieval */
+/* Client and Channel entry retrieval (idlist.c) */
 
 /* Callback function given to the silc_client_get_client function. The
    found entries are allocated into the `clients' array. The array must
@@ -328,7 +344,7 @@ SilcChannelEntry silc_client_get_channel(SilcClient client,
                                         char *channel);
 
 
-/* Command management */
+/* Command management (command.c) */
 
 /* Allocate Command Context. The context is defined in `command.h' file.
    The context is used by the library commands and applications should use
@@ -374,7 +390,7 @@ void silc_client_command_pending(SilcClientConnection conn,
                                 void *context);
 
 
-/* Private Message key management */
+/* Private Message key management (client_prvmsg.c) */
 
 /* Adds private message key to the client library. The key will be used to
    encrypt all private message between the client and the remote client
@@ -462,7 +478,7 @@ void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
                                           unsigned int key_count);
 
 
-/* Channel private key management */
+/* Channel private key management (client_channel.c) */
 
 /* Adds private key for channel. This may be set only if the channel's mode
    mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
@@ -529,27 +545,14 @@ SilcChannelPrivateKey *
 silc_client_list_channel_private_keys(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcChannelEntry channel,
-                                     unsigned int key_count);
+                                     unsigned int *key_count);
 
 /* Frees the SilcChannelPrivateKey array. */
 void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
                                           unsigned int key_count);
 
 
-/* Key Agreement routines */
-
-/* Key agreement callback that is called after the key agreement protocol
-   has been performed. This is called also if error occured during the
-   key agreement protocol. The `key' is the allocated key material and
-   the caller is responsible of freeing it. The `key' is NULL if error
-   has occured. The application can freely use the `key' to whatever
-   purpose it needs. See lib/silcske/silcske.h for the definition of
-   the SilcSKEKeyMaterial structure. */
-typedef void (*SilcKeyAgreementCallback)(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcClientEntry client_entry,
-                                        SilcSKEKeyMaterial *key,
-                                        void *context);
+/* Key Agreement routines (client_keyagr.c) */
 
 /* Sends key agreement request to the remote client indicated by the
    `client_entry'. If the caller provides the `hostname' and the `port'
@@ -576,14 +579,19 @@ typedef void (*SilcKeyAgreementCallback)(SilcClient client,
    or decides not to reply with the key agreement packet then we cannot
    perform the key agreement at all. If the key agreement protocol is
    performed the `completion' callback with the `context' will be called.
-   If remote side decides to ignore the request the `completion' will never
-   be called and the caller is responsible of freeing the `context' memory. 
-   The application can do this by setting, for example, timeout. */
+   If remote side decides to ignore the request the `completion' will be
+   called after the specified timeout, `timeout_secs'. 
+
+   NOTE: There can be only one active key agreement for one client entry.
+   Before setting new one, the old one must be finished (it is finished
+   after calling the completion callback) or the function 
+   silc_client_abort_key_agreement must be called. */
 void silc_client_send_key_agreement(SilcClient client,
                                    SilcClientConnection conn,
                                    SilcClientEntry client_entry,
                                    char *hostname,
                                    int port,
+                                   unsigned long timeout_secs,
                                    SilcKeyAgreementCallback completion,
                                    void *context);
 
@@ -593,7 +601,7 @@ void silc_client_send_key_agreement(SilcClient client,
    and did not return TRUE from it.
 
    The `hostname' is the remote hostname (or IP address) and the `port'
-   is the remote port. The `completion' callblack with the `context' will
+   is the remote port. The `completion' callback with the `context' will
    be called after the key agreement protocol.
    
    NOTE: If the application returns TRUE in the `key_agreement' client
@@ -614,6 +622,17 @@ void silc_client_perform_key_agreement(SilcClient client,
                                       SilcKeyAgreementCallback completion,
                                       void *context);
 
+/* Same as above but application has created already the connection to 
+   the remote host. The `sock' is the socket to the remote connection. 
+   Application can use this function if it does not want the client library
+   to create the connection. */
+void silc_client_perform_key_agreement_fd(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         int sock,
+                                         SilcKeyAgreementCallback completion,
+                                         void *context);
+
 /* This function can be called to unbind the hostname and the port for
    the key agreement protocol. However, this function has effect only 
    before the key agreement protocol has been performed. After it has
index cd08d5edf7545f8b27d598c7ce8b7ce336b975af..153058609f607080e151ab1a7493ae95abe9c3f6 100644 (file)
@@ -329,7 +329,7 @@ SilcBuffer silc_key_agreement_payload_encode(char *hostname,
                                             unsigned int port)
 {
   SilcBuffer buffer;
-  unsigned int len = strlen(hostname);
+  unsigned int len = hostname ? strlen(hostname) : 0;
 
   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
 
index 61929fa8b0028a60f8fd4b8aa2eb62aa257207c8..54409ec1ae1e9b792a2084b7202bd63b3750ac70 100644 (file)
 
 /******************************************************************************
 
-                              Channel Payload
+                          Channel Message Payload
 
 ******************************************************************************/
 
-/* Channel Payload structure. Contents of this structure is parsed
+/* Channel Message Payload structure. Contents of this structure is parsed
    from SILC packets. */
 struct SilcChannelPayloadStruct {
   unsigned short data_len;
   unsigned char *data;
-  unsigned short iv_len;
+  unsigned char *mac;
   unsigned char *iv;
 };
 
-/* Parses channel payload returning new channel payload structure */
+/* Parses channel payload returning new channel payload structure. This
+   also decrypts it and checks the MAC. */
 
-SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer)
+SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
+                                             SilcCipher cipher,
+                                             SilcHmac hmac)
 {
   SilcChannelPayload new;
   int ret;
+  unsigned int iv_len, mac_len;
+  unsigned char *mac, mac2[32];
 
   SILC_LOG_DEBUG(("Parsing channel payload"));
 
+  /* Decrypt the channel message. First push the IV out of the packet.
+     The IV is used in the decryption process. Then decrypt the message.
+     After decyprtion, take the MAC from the decrypted packet, compute MAC
+     and compare the MACs.  If they match, the decryption was successfull
+     and we have the channel message ready to be displayed. */
+
+  /* Push the IV out of the packet (it will be in buffer->tail) */
+  iv_len = silc_cipher_get_block_len(cipher);
+  silc_buffer_push_tail(buffer, iv_len);
+
+  /* Decrypt the channel message */
+  silc_cipher_decrypt(cipher, buffer->data, buffer->data,
+                     buffer->len, buffer->tail);
+
+  /* Take the MAC */
+  mac_len = silc_hmac_len(hmac);
+  silc_buffer_push_tail(buffer, mac_len);
+  mac = buffer->tail;
+
+  /* Check the MAC of the message */
+  SILC_LOG_DEBUG(("Checking channel message MACs"));
+  silc_hmac_make(hmac, buffer->data, buffer->len, mac2, &mac_len);
+  if (memcmp(mac, mac2, mac_len)) {
+    SILC_LOG_DEBUG(("Channel message MACs does not match"));
+    return NULL;
+  }
+  SILC_LOG_DEBUG(("MAC is Ok"));
+  silc_buffer_pull_tail(buffer, iv_len + mac_len);
+
   new = silc_calloc(1, sizeof(*new));
 
-  /* Parse the Channel Payload. Ignore padding and IV, we don't need
-     them. */
+  /* Parse the Channel Payload. Ignore the padding. */
   ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI16_NSTRING_ALLOC(&new->data, 
-                                                        &new->data_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(NULL, NULL),
+                            SILC_STR_UI16_NSTRING(&new->data, 
+                                                  &new->data_len),
+                            SILC_STR_UI16_NSTRING(NULL, NULL),
+                            SILC_STR_UI_XNSTRING(&new->mac, mac_len),
+                            SILC_STR_UI_XNSTRING(&new->iv, iv_len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
@@ -67,10 +102,6 @@ SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer)
   return new;
 
  err:
-  if (new->data)
-    silc_free(new->data);
-  if (new->iv)
-    silc_free(new->iv);
   silc_free(new);
   return NULL;
 }
@@ -84,38 +115,59 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len,
                                       unsigned char *data,
                                       unsigned short iv_len,
                                       unsigned char *iv,
+                                      SilcCipher cipher,
+                                      SilcHmac hmac,
                                       SilcRng rng)
 {
   int i;
   SilcBuffer buffer;
-  unsigned int len, pad_len;
+  unsigned int len, pad_len, mac_len;
   unsigned char pad[SILC_PACKET_MAX_PADLEN];
+  unsigned char mac[32];
 
   SILC_LOG_DEBUG(("Encoding channel payload"));
 
   /* Calculate length of padding. IV is not included into the calculation
      since it is not encrypted. */
-  len = 2 + data_len + 2;
+  mac_len = silc_hmac_len(hmac);
+  len = 4 + data_len + mac_len;
   pad_len = SILC_PACKET_PADLEN((len + 2));
 
   /* Allocate channel payload buffer */
-  len += pad_len;
-  buffer = silc_buffer_alloc(len + iv_len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  len += pad_len + iv_len;
+  buffer = silc_buffer_alloc(len);
 
   /* Generate padding */
   for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte(rng);
 
   /* Encode the Channel Payload */
+  silc_buffer_pull_tail(buffer, 4 + data_len + pad_len);
   silc_buffer_format(buffer, 
                     SILC_STR_UI_SHORT(data_len),
                     SILC_STR_UI_XNSTRING(data, data_len),
                     SILC_STR_UI_SHORT(pad_len),
                     SILC_STR_UI_XNSTRING(pad, pad_len),
+                    SILC_STR_END);
+
+  /* Compute the MAC of the channel message data */
+  silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
+
+  /* Put rest of the data to the payload */
+  silc_buffer_pull_tail(buffer, mac_len + iv_len);
+  silc_buffer_pull(buffer, 4 + data_len + pad_len);
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_XNSTRING(mac, mac_len),
                     SILC_STR_UI_XNSTRING(iv, iv_len),
                     SILC_STR_END);
+  silc_buffer_push(buffer, 4 + data_len + pad_len);
+
+  /* Encrypt payload of the packet. This is encrypted with the channel key. */
+  silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
+                     buffer->len - iv_len, iv);
+
+  memset(pad, 0, sizeof(pad));
+  memset(mac, 0, sizeof(mac));
 
-  memset(pad, 0, pad_len);
   return buffer;
 }
 
@@ -123,13 +175,8 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len,
 
 void silc_channel_payload_free(SilcChannelPayload payload)
 {
-  if (payload) {
-    if (payload->data)
-      silc_free(payload->data);
-    if (payload->iv)
-      silc_free(payload->iv);
+  if (payload)
     silc_free(payload);
-  }
 }
 
 /* Return data */
@@ -143,14 +190,17 @@ unsigned char *silc_channel_get_data(SilcChannelPayload payload,
   return payload->data;
 }
 
-/* Return initial vector */
+/* Return MAC. The caller knows the length of the MAC */
 
-unsigned char *silc_channel_get_iv(SilcChannelPayload payload,
-                                  unsigned int *iv_len)
+unsigned char *silc_channel_get_mac(SilcChannelPayload payload)
 {
-  if (iv_len)
-    *iv_len = payload->iv_len;
+  return payload->mac;
+}
+
+/* Return IV. The caller knows the length of the IV */
 
+unsigned char *silc_channel_get_iv(SilcChannelPayload payload)
+{
   return payload->iv;
 }
 
index 6169fe9a2662c423c5fdbecca158b608d9fa5150..2ac4d7f68d5b5b8fa8c97942dc6388d0d908da8e 100644 (file)
@@ -21,7 +21,7 @@
 #ifndef SILCCHANNEL_H
 #define SILCCHANNEL_H
 
-/* Forward declaration for Channel Payload parsed from packet. The
+/* Forward declaration for Channel Message Payload parsed from packet. The
    actual structure is defined in source file and is private data. */
 typedef struct SilcChannelPayloadStruct *SilcChannelPayload;
 
@@ -30,17 +30,21 @@ typedef struct SilcChannelPayloadStruct *SilcChannelPayload;
 typedef struct SilcChannelKeyPayloadStruct *SilcChannelKeyPayload;
 
 /* Prototypes */
-SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer);
+SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
+                                             SilcCipher cipher,
+                                             SilcHmac hmac);
 SilcBuffer silc_channel_payload_encode(unsigned short data_len,
                                       unsigned char *data,
                                       unsigned short iv_len,
                                       unsigned char *iv,
+                                      SilcCipher cipher,
+                                      SilcHmac hmac,
                                       SilcRng rng);
 void silc_channel_payload_free(SilcChannelPayload payload);
 unsigned char *silc_channel_get_data(SilcChannelPayload payload,
                                     unsigned int *data_len);
-unsigned char *silc_channel_get_iv(SilcChannelPayload payload,
-                                  unsigned int *iv_len);
+unsigned char *silc_channel_get_mac(SilcChannelPayload payload);
+unsigned char *silc_channel_get_iv(SilcChannelPayload payload);
 SilcChannelKeyPayload silc_channel_key_payload_parse(SilcBuffer buffer);
 SilcBuffer silc_channel_key_payload_encode(unsigned short id_len,
                                           unsigned char *id,
index 53d04ee4b55ca52c0749f0957fe3f9d79f89c93c..6949845bc08c545c07755fb3276cbd8c8edabdc3 100644 (file)
@@ -32,7 +32,8 @@
 #define SILC_CHANNEL_MODE_PASSPHRASE  0x0040 /* passphrase set */
 #define SILC_CHANNEL_MODE_BAN         0x0080 /* ban list set */
 #define SILC_CHANNEL_MODE_INVITE_LIST 0x0100 /* invite list set */
-#define SILC_CHANNEL_MODE_CIPHER      0x0200 /* sets cipher of channel */
+#define SILC_CHANNEL_MODE_CIPHER      0x0200 /* sets cipher of the channel */
+#define SILC_CHANNEL_MODE_HMAC        0x0400 /* sets hmac of the channel */
 
 /* User modes on channel */
 #define SILC_CHANNEL_UMODE_NONE       0x0000 /* Normal user */
index 39e2865a080b88817bcb596042b825b27d9e337d..f331cb09d75b7328db0614f0e6b9ac7bf68e88d7 100644 (file)
@@ -112,13 +112,14 @@ void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac,
                         SilcBuffer buffer, unsigned int len)
 {
   unsigned char mac[32];
+  unsigned int mac_len;
 
   /* Compute HMAC. This assumes that HMAC is created from the entire
      data area thus this uses the length found in buffer, not the length
      sent as argument. */
   if (hmac) {
-    silc_hmac_make(hmac, buffer->data, buffer->len, mac);
-    silc_buffer_put_tail(buffer, mac, hmac->hash->hash->hash_len);
+    silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
+    silc_buffer_put_tail(buffer, mac, mac_len);
     memset(mac, 0, sizeof(mac));
   }
 
@@ -133,7 +134,7 @@ void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac,
 
   /* Pull the HMAC into the visible data area in the buffer */
   if (hmac)
-    silc_buffer_pull_tail(buffer, hmac->hash->hash->hash_len);
+    silc_buffer_pull_tail(buffer, mac_len);
 }
 
 /* Assembles a new packet to be ready for send out. The buffer sent as
@@ -378,7 +379,7 @@ void silc_packet_receive_process(SilcSocketConnection sock,
     return;
 
   if (hmac)
-    mac_len = hmac->hash->hash->hash_len;
+    mac_len = hmac->hmac->len;
 
   /* Parse the packets from the data */
   count = 0;
@@ -465,15 +466,16 @@ static int silc_packet_check_mac(SilcHmac hmac, SilcBuffer buffer)
   /* Check MAC */
   if (hmac) {
     unsigned char mac[32];
+    unsigned int mac_len;
     
     SILC_LOG_DEBUG(("Verifying MAC"));
 
     /* Compute HMAC of packet */
     memset(mac, 0, sizeof(mac));
-    silc_hmac_make(hmac, buffer->data, buffer->len, mac);
+    silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
 
     /* Compare the HMAC's (buffer->tail has the packet's HMAC) */
-    if (memcmp(mac, buffer->tail, hmac->hash->hash->hash_len)) {
+    if (memcmp(mac, buffer->tail, mac_len)) {
       SILC_LOG_DEBUG(("MAC failed"));
       return FALSE;
     }
@@ -497,8 +499,8 @@ static int silc_packet_decrypt_rest(SilcCipher cipher, SilcHmac hmac,
 
     /* Pull MAC from packet before decryption */
     if (hmac) {
-      if ((buffer->len - hmac->hash->hash->hash_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, hmac->hash->hash->hash_len);
+      if ((buffer->len - hmac->hmac->len) > SILC_PACKET_MIN_LEN) {
+       silc_buffer_push_tail(buffer, hmac->hmac->len);
       } else {
        SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
        return FALSE;
@@ -537,8 +539,8 @@ static int silc_packet_decrypt_rest_special(SilcCipher cipher,
 
     /* Pull MAC from packet before decryption */
     if (hmac) {
-      if ((buffer->len - hmac->hash->hash->hash_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, hmac->hash->hash->hash_len);
+      if ((buffer->len - hmac->hmac->len) > SILC_PACKET_MIN_LEN) {
+       silc_buffer_push_tail(buffer, hmac->hmac->len);
       } else {
        SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
        return FALSE;
index 89d6244300a31237db25361c65a1cfd4c00f7446..38ca3c9d45176e5ba1e445b5a0e5da8ac3a97b09 100644 (file)
@@ -92,6 +92,7 @@ void silc_protocol_alloc(SilcProtocolType type, SilcProtocol *new_protocol,
 
   if (!protocol) {
     SILC_LOG_ERROR(("Requested protocol does not exists"));
+    *new_protocol = NULL;
     return;
   }
 
index 74b8bb98a3d5167f3fa55842a861bad5b98215b9..5eba8bcae6562293268833c56968a90cd452635c 100644 (file)
@@ -93,7 +93,7 @@ int silc_cipher_register(SilcCipherObject *cipher)
 {
   struct SilcCipherListStruct *new, *c;
 
-  SILC_LOG_DEBUG(("Registering new cipher"));
+  SILC_LOG_DEBUG(("Registering new cipher `%s'", cipher->name));
 
   new = silc_calloc(1, sizeof(*new));
   new->cipher = silc_calloc(1, sizeof(*new->cipher));
@@ -314,6 +314,24 @@ char *silc_cipher_get_supported()
   return list;
 }
 
+/* Encrypts */
+
+int silc_cipher_encrypt(SilcCipher cipher, const unsigned char *src,
+                       unsigned char *dst, unsigned int len, 
+                       unsigned char *iv)
+{
+  return cipher->cipher->encrypt(cipher->context, src, dst, len, iv);
+}
+
+/* Decrypts */
+
+int silc_cipher_decrypt(SilcCipher cipher, const unsigned char *src,
+                       unsigned char *dst, unsigned int len, 
+                       unsigned char *iv)
+{
+  return cipher->cipher->decrypt(cipher->context, src, dst, len, iv);
+}
+
 /* Sets the key for the cipher */
 
 int silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
@@ -340,8 +358,7 @@ void silc_cipher_get_iv(SilcCipher cipher, unsigned char *iv)
 
 /* Returns the key length of the cipher. */
 
-unsigned int silc_cipher_get_key_len(SilcCipher cipher, 
-                                    const unsigned char *name)
+unsigned int silc_cipher_get_key_len(SilcCipher cipher)
 {
   return cipher->cipher->key_len;
 }
index 6eea2c9c0a647ba9eb4fcf7a040d05c437c98256..65ef26eee2837ee468d4f0dd6239dc0cc1b5aafd 100644 (file)
@@ -122,12 +122,17 @@ int silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher);
 void silc_cipher_free(SilcCipher cipher);
 int silc_cipher_is_supported(const unsigned char *name);
 char *silc_cipher_get_supported();
+int silc_cipher_encrypt(SilcCipher cipher, const unsigned char *src,
+                       unsigned char *dst, unsigned int len, 
+                       unsigned char *iv);
+int silc_cipher_decrypt(SilcCipher cipher, const unsigned char *src,
+                       unsigned char *dst, unsigned int len, 
+                       unsigned char *iv);
 int silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
                        unsigned int keylen);
 void silc_cipher_set_iv(SilcCipher cipher, const unsigned char *iv);
 void silc_cipher_get_iv(SilcCipher cipher, unsigned char *iv);
-unsigned int silc_cipher_get_key_len(SilcCipher cipher, 
-                                    const unsigned char *name);
+unsigned int silc_cipher_get_key_len(SilcCipher cipher);
 unsigned int silc_cipher_get_block_len(SilcCipher cipher);
 
 #endif
index 58cecec9cb44225d0b0f2b450fe1ebb8dc6db15e..528b435387731e023dd3da1549e3d2a34e413a97 100644 (file)
@@ -38,29 +38,28 @@ struct SilcHashListStruct *silc_hash_list = NULL;
 /* Statically declared list of hash functions. */
 SilcHashObject silc_hash_builtin_list[] = 
 {
-  { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
-    silc_md5_transform, silc_md5_context_len },
   { "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final,
     silc_sha1_transform, silc_sha1_context_len },
+  { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
+    silc_md5_transform, silc_md5_context_len },
 
   { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }
 };
 
-/* Registers a ned hash function into the SILC. This function is used at
+/* Registers a new hash function into the SILC. This function is used at
    the initialization of the SILC. */
 
 int silc_hash_register(SilcHashObject *hash)
 {
   struct SilcHashListStruct *new, *h;
 
-  SILC_LOG_DEBUG(("Registering new hash function"));
+  SILC_LOG_DEBUG(("Registering new hash function `%s'", hash->name));
 
   new = silc_calloc(1, sizeof(*new));
   new->hash = silc_calloc(1, sizeof(*new->hash));
 
   /* Set the pointers */
-  new->hash->name = silc_calloc(1, strlen(hash->name));
-  memcpy(new->hash->name, hash->name, strlen(hash->name));
+  new->hash->name = strdup(hash->name);
   new->hash->hash_len = hash->hash_len;
   new->hash->block_len = hash->block_len;
   new->hash->init = hash->init;
@@ -196,12 +195,22 @@ void silc_hash_free(SilcHash hash)
   }
 }
 
+/* Returns the length of the hash digest. */
+
+unsigned int silc_hash_len(SilcHash hash)
+{
+  return hash->hash->hash_len;
+}
+
 /* Returns TRUE if hash algorithm `name' is supported. */
 
 int silc_hash_is_supported(const unsigned char *name)
 {
   struct SilcHashListStruct *h;
   int i;
+
+  if (!name)
+    return FALSE;
   
   if (silc_hash_list) {
     h = silc_hash_list;
index 5f4ea0f3e086970e33249610b13c8b8340062805..4ee592ab6a565edd6fa9be2ef0b306de209c4dc7 100644 (file)
@@ -84,6 +84,7 @@ int silc_hash_register(SilcHashObject *hash);
 int silc_hash_unregister(SilcHashObject *hash);
 int silc_hash_alloc(const unsigned char *name, SilcHash *new_hash);
 void silc_hash_free(SilcHash hash);
+unsigned int silc_hash_len(SilcHash hash);
 int silc_hash_is_supported(const unsigned char *name);
 char *silc_hash_get_supported();
 void silc_hash_make(SilcHash hash, const unsigned char *data,
index 347894cb44be0d7c1603923aa93a8c5e565718f3..1edfb3813049610adf3f17f67cc704ea9a2b2b77 100644 (file)
 
 #include "silcincludes.h"
 
-/* Allocates a new SilcHmac object. First argument is the hash function
-   object to tell the hmac which hash function should be used when creating
-   HMAC's. The new SilcHmac object is returned to new_hmac argument. */
+/* List of dynamically registered HMACs. */
+SilcDList silc_hmac_list = NULL;
 
-int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac)
+/* Registers a new HMAC into the SILC. This function is used at the
+   initialization of the SILC. */
+
+int silc_hmac_register(SilcHmacObject *hmac)
+{
+  SilcHmacObject *new;
+
+  SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
+
+  new = silc_calloc(1, sizeof(*new));
+  new->name = strdup(hmac->name);
+  new->len = hmac->len;
+
+  /* Add to list */
+  if (silc_hmac_list == NULL)
+    silc_hmac_list = silc_dlist_init();
+  silc_dlist_add(silc_hmac_list, new);
+
+  return TRUE;
+}
+
+/* Unregister a HMAC from the SILC. */
+
+int silc_hmac_unregister(SilcHmacObject *hmac)
 {
-  SILC_LOG_DEBUG(("Allocating new hmac object"));
+  SilcHmacObject *entry;
+
+  SILC_LOG_DEBUG(("Unregistering HMAC"));
+
+  if (!silc_hmac_list)
+    return FALSE;
 
+  silc_dlist_start(silc_hmac_list);
+  while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+    if (entry == hmac) {
+      silc_dlist_del(silc_hmac_list, entry);
+
+      if (silc_dlist_count(silc_hmac_list) == 0) {
+       silc_dlist_uninit(silc_hmac_list);
+       silc_hmac_list = NULL;
+      }
+
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Allocates a new SilcHmac object of name of `name'.  The `hash' may
+   be provided as argument.  If provided it is used as the hash function
+   of the HMAC.  If it is NULL then the hash function is allocated and
+   the name of the hash algorithm is derived from the `name'. */
+
+int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
+{
+  SilcHmacObject *entry;
+
+  SILC_LOG_DEBUG(("Allocating new HMAC"));
+
+  /* Allocate the new object */
   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
+
+  if (!hash) {
+    char *tmp = strdup(name), *hname;
+
+    hname = tmp;
+    if (strchr(hname, '-'))
+      hname = strchr(hname, '-') + 1;
+    if (strchr(hname, '-'))
+      *strchr(hname, '-') = '\0';
+
+    if (!silc_hash_alloc(hname, &hash)) {
+      silc_free(tmp);
+      return FALSE;
+    }
+
+    (*new_hmac)->allocated_hash = TRUE;
+    silc_free(tmp);
+  }
+
   (*new_hmac)->hash = hash;
-  (*new_hmac)->set_key = silc_hmac_set_key;
-  (*new_hmac)->make_hmac = silc_hmac_make;
-  (*new_hmac)->make_hmac_with_key = silc_hmac_make_with_key;
-  (*new_hmac)->make_hmac_truncated = silc_hmac_make_truncated;
 
-  return TRUE;
+  if (silc_hmac_list) {
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name)) {
+       (*new_hmac)->hmac = entry; 
+       return TRUE;
+      }
+    }
+  }
+
+  return FALSE;
 }
 
 /* Free's the SilcHmac object. */
 
 void silc_hmac_free(SilcHmac hmac)
 {
-  if (hmac)
+  if (hmac) {
+    if (hmac->allocated_hash)
+      silc_hash_free(hmac->hash);
     silc_free(hmac);
+  }
+}
+
+/* Returns the length of the MAC that the HMAC will produce. */
+
+unsigned int silc_hmac_len(SilcHmac hmac)
+{
+  return hmac->hmac->len;
+}
+
+/* Returns TRUE if HMAC `name' is supported. */
+
+int silc_hmac_is_supported(const char *name)
+{
+  SilcHmacObject *entry;
+
+  if (!name)
+    return FALSE;
+  
+  if (silc_hmac_list) {
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name))
+       return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Returns comma separated list of supported HMACs. */
+
+char *silc_hmac_get_supported()
+{
+  SilcHmacObject *entry;
+  char *list = NULL;
+  int len;
+
+  len = 0;
+  if (silc_hmac_list) {
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      len += strlen(entry->name);
+      list = silc_realloc(list, len + 1);
+      
+      memcpy(list + (len - strlen(entry->name)), 
+            entry->name, strlen(entry->name));
+      memcpy(list + len, ",", 1);
+      len++;
+    }
+  }
+
+  list[len - 1] = 0;
+
+  return list;
+}
+
+/* Sets the HMAC key used in the HMAC creation */
+
+void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
+                      unsigned int key_len)
+{
+  hmac->key = silc_calloc(key_len, sizeof(unsigned char));
+  hmac->key_len = key_len;
+  memcpy(hmac->key, key, key_len);
 }
 
 /* Creates the HMAC. The created keyed hash value is returned to 
@@ -58,6 +206,7 @@ void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
   unsigned char inner_pad[hash->hash->block_len + 1];
   unsigned char outer_pad[hash->hash->block_len + 1];
   unsigned char hvalue[hash->hash->hash_len];
+  unsigned char mac[128];
   void *hash_context;
   int i;
 
@@ -71,7 +220,7 @@ void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
   /* If the key length is more than block size of the hash function, the
      key is hashed. */
   if (key_len > hash->hash->block_len) {
-    hash->make_hash(hash, key, key_len, hvalue);
+    silc_hash_make(hash, key, key_len, hvalue);
     key = hvalue;
     key_len = hash->hash->hash_len;
   }
@@ -90,21 +239,40 @@ void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
   hash->hash->init(hash_context);
   hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
   hash->hash->update(hash_context, data, data_len);
-  hash->hash->final(hash_context, return_hash);
+  hash->hash->final(hash_context, mac);
   hash->hash->init(hash_context);
   hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
-  hash->hash->update(hash_context, return_hash, hash->hash->hash_len);
-  hash->hash->final(hash_context, return_hash);
+  hash->hash->update(hash_context, mac, hash->hash->hash_len);
+  hash->hash->final(hash_context, mac);
+  memcpy(return_hash, mac, hmac->hmac->len);
+  memset(mac, 0, sizeof(mac));
 }
 
 /* Create the HMAC. This is thee make_hmac function pointer.  This
    uses the internal key set with silc_hmac_set_key. */
 
 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
-                   unsigned int data_len, unsigned char *return_hash)
+                   unsigned int data_len, unsigned char *return_hash,
+                   unsigned int *return_len)
 {
   silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
                          hmac->key_len, return_hash);
+  if (return_len)
+    *return_len = hmac->hmac->len;
+}
+
+/* Creates HMAC just as above except that this doesn't use the internal
+   key. The key is sent as argument to the function. */
+
+void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
+                            unsigned int data_len, 
+                            unsigned char *key, unsigned int key_len,
+                            unsigned char *return_hash,
+                            unsigned int *return_len)
+{
+  silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
+  if (return_len)
+    *return_len = hmac->hmac->len;
 }
 
 /* Creates the HMAC just as above except that the hash value is truncated
@@ -124,24 +292,3 @@ void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
   memcpy(return_hash, hvalue, truncated_len);
   memset(hvalue, 0, sizeof(hvalue));
 }
-
-/* Creates HMAC just as above except that this doesn't use the internal
-   key. The key is sent as argument to the function. */
-
-void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
-                            unsigned int data_len, 
-                            unsigned char *key, unsigned int key_len,
-                            unsigned char *return_hash)
-{
-  silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
-}
-
-/* Sets the HMAC key used in the HMAC creation */
-
-void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
-                      unsigned int key_len)
-{
-  hmac->key = silc_calloc(key_len, sizeof(unsigned char));
-  hmac->key_len = key_len;
-  memcpy(hmac->key, key, key_len);
-}
index 069476e9e21df80ebfd2838d716e44aaf921b8ec..19f8505277bdbe6aace6868753ef038265479118 100644 (file)
    routines were created according to RFC2104. Following short description 
    of the fields:
 
-   SilcHash hash
+   SilcHmacObject:
 
-       The hash object to tell what hash function to use with this HMAC.
+   char *name
+
+       Name of the HMAC.
 
-   unsigned char *key
    unsigned int len
 
-       The key and its length used to make the HMAC. This is set
-       with silc_hmac_set_key function.
+       Length of the MAC the HMAC is to produce (bytes).
 
-   void (*set_key)(SilcHmac, const unsigned char *, unsigned int)
 
-       Function used to set the key for the HMAC. Second argument is
-       the key to be set and last argument is the length of the key.
+   SilcHmac:
 
-   void (*make_hmac)(SilcHmac, unsigned char *, unsigned int,
-                     unsigned char *)
+   SilcHash hash
 
-       Function what is used to create HMAC's. User can also use directly
-       silc_hmac_make fuction. Although, one needs to allocate a SilcHmac
-       object before doing it, naturally. This uses the key set with
-       silc_hmac_set_key function.
+       The hash object to tell what hash function to use with this HMAC.
 
-   void (*make_hmac_with_key)(SilcHmac, unsigned char *, unsigned int,
-                              unsigned char *, unsigned int, unsigned char *)
+   char allocated_hash
 
-       Same function as above except that the key used in the HMAC
-       creation is sent as argument. The key set with silc_hmac_set_key
-       is ignored in this case.
+       TRUE if the `hash' was allocated and FALSE if it is static and
+       must not be freed.
 
-   void (*make_hmac_truncated)(SilcHmac, unsigned char *, unsigned int,
-                              unsigned int, unsigned char *)
+   unsigned char *key
+   unsigned int len
 
-       Same function as above except that the output hash value is truncated
-       to the length sent as argument (second last argument). This makes
-       variable truncations possible, however, one should not truncate
-       hash values to less than half of the length of the hash value.
+       The key and its length used to make the HMAC. This is set
+       with silc_hmac_set_key function.
 
 */
 typedef struct SilcHmacStruct *SilcHmac;
 
+typedef struct {
+  char *name;
+  unsigned int len;
+} SilcHmacObject;
+
 struct SilcHmacStruct {
+  SilcHmacObject *hmac;
   SilcHash hash;
+  char allocated_hash;
   unsigned char *key;
   unsigned int key_len;
-  void (*set_key)(SilcHmac, const unsigned char *, unsigned int);
-  void (*make_hmac)(SilcHmac, unsigned char *, unsigned int,
-                   unsigned char *);
-  void (*make_hmac_with_key)(SilcHmac, unsigned char *, unsigned int,
-                            unsigned char *, unsigned int, unsigned char *);
-  void (*make_hmac_truncated)(SilcHmac, unsigned char *, 
-                             unsigned int, unsigned int, unsigned char *);
 };
 
 /* Prototypes */
-int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac);
+int silc_hmac_register(SilcHmacObject *hmac);
+int silc_hmac_unregister(SilcHmacObject *hmac);
+int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac);
 void silc_hmac_free(SilcHmac hmac);
+int silc_hmac_is_supported(const char *name);
+char *silc_hmac_get_supported();
+unsigned int silc_hmac_len(SilcHmac hmac);
 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
                       unsigned int key_len);
-void silc_hmac_make(SilcHmac hmac, 
-                   unsigned char *data, 
-                   unsigned int data_len,
-                   unsigned char *return_hash);
-void silc_hmac_make_with_key(SilcHmac hmac, 
-                            unsigned char *data, 
-                            unsigned int data_len,
-                            unsigned char *key, 
-                            unsigned int key_len, 
-                            unsigned char *return_hash);
+void silc_hmac_make(SilcHmac hmac, unsigned char *data,
+                   unsigned int data_len, unsigned char *return_hash,
+                   unsigned int *return_len);
+void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
+                            unsigned int data_len, 
+                            unsigned char *key, unsigned int key_len,
+                            unsigned char *return_hash,
+                            unsigned int *return_len);
 void silc_hmac_make_truncated(SilcHmac hmac, 
                              unsigned char *data, 
                              unsigned int data_len,
index a5475074da39e44019dfed00817f881d2b29f873..3fc2d4c7782cad4c3395aeee1467218ff83d3f39 100644 (file)
@@ -96,6 +96,9 @@ int silc_pkcs_is_supported(const unsigned char *name)
 {
   int i;
 
+  if (!name)
+    return FALSE;
+
   for (i = 0; silc_pkcs_list[i].name; i++) {
     if (!strcmp(silc_pkcs_list[i].name, name))
       return TRUE;
index d0526135f27d38fec05c735fa5b1c8a6eefbb0b0..acb4d277666ba1f2450063502372671ceffee423 100644 (file)
@@ -62,6 +62,9 @@ SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
                           SILC_STR_UI_SHORT(payload->hash_alg_len),
                           SILC_STR_UI_XNSTRING(payload->hash_alg_list,
                                                payload->hash_alg_len),
+                          SILC_STR_UI_SHORT(payload->hmac_alg_len),
+                          SILC_STR_UI_XNSTRING(payload->hmac_alg_list,
+                                               payload->hmac_alg_len),
                           SILC_STR_UI_SHORT(payload->comp_alg_len),
                           SILC_STR_UI_XNSTRING(payload->comp_alg_list,
                                                payload->comp_alg_len),
@@ -204,7 +207,7 @@ silc_ske_payload_start_decode(SilcSKE ske,
     silc_buffer_unformat(buffer,
                         SILC_STR_UI_XNSTRING_ALLOC(&payload->hash_alg_list, 
                                                    payload->hash_alg_len),
-                        SILC_STR_UI_SHORT(&payload->comp_alg_len),
+                        SILC_STR_UI_SHORT(&payload->hmac_alg_len),
                         SILC_STR_END);
   if (ret == -1) {
     status = SILC_SKE_STATUS_ERROR;
@@ -214,6 +217,21 @@ silc_ske_payload_start_decode(SilcSKE ske,
   len2 += len = payload->hash_alg_len + 2;
   silc_buffer_pull(buffer, len);
 
+  /* Parse HMAC list */
+  ret = 
+    silc_buffer_unformat(buffer,
+                        SILC_STR_UI_XNSTRING_ALLOC(&payload->hmac_alg_list, 
+                                                   payload->hmac_alg_len),
+                        SILC_STR_UI_SHORT(&payload->comp_alg_len),
+                        SILC_STR_END);
+  if (ret == -1) {
+    status = SILC_SKE_STATUS_ERROR;
+    goto err;
+  }
+
+  len2 += len = payload->hmac_alg_len + 2;
+  silc_buffer_pull(buffer, len);
+
   /* Parse compression alg list */
   if (payload->comp_alg_len) {
     ret = 
@@ -258,6 +276,8 @@ void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
       silc_free(payload->enc_alg_list);
     if (payload->hash_alg_list)
       silc_free(payload->hash_alg_list);
+    if (payload->hmac_alg_list)
+      silc_free(payload->hmac_alg_list);
     if (payload->comp_alg_list)
       silc_free(payload->comp_alg_list);
     silc_free(payload);
index c91b3aeedf860de0ba64f4eb9c916dd16cae07eb..56bf497af7365e1dfb0bafca508a10e8aafc9020 100644 (file)
@@ -44,6 +44,9 @@ typedef struct {
   unsigned short hash_alg_len;
   unsigned char *hash_alg_list;
 
+  unsigned short hmac_alg_len;
+  unsigned char *hmac_alg_list;
+
   unsigned short comp_alg_len;
   unsigned char *comp_alg_list;
 } SilcSKEStartPayload;
index 234d88d826aa8e9ce6d3cd55c82335e6a8bf84d0..c0b035e61a28781cbba10df0c382e66255f6bd75 100644 (file)
@@ -66,6 +66,8 @@ void silc_ske_free(SilcSKE ske)
        silc_cipher_free(ske->prop->cipher);
       if (ske->prop->hash)
        silc_hash_free(ske->prop->hash);
+      if (ske->prop->hmac)
+       silc_hmac_free(ske->prop->hmac);
       silc_free(ske->prop);
     }
     if (ske->start_payload_copy)
@@ -181,6 +183,11 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
     goto err;
   }
 
+  if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
+    status = SILC_SKE_STATUS_UNKNOWN_HMAC;
+    goto err;
+  }
+
   ske->start_payload = payload;
 
   /* Return the received payload by calling the callback function. */
@@ -201,6 +208,8 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
     silc_cipher_free(prop->cipher);
   if (prop->hash)
     silc_hash_free(prop->hash);
+  if (prop->hmac)
+    silc_hmac_free(prop->hmac);
   silc_free(prop);
   ske->prop = NULL;
 
@@ -501,6 +510,12 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
     goto err;
   }
 
+  if (silc_hmac_alloc(start_payload->hmac_alg_list, NULL,
+                     &prop->hmac) == FALSE) {
+    status = SILC_SKE_STATUS_UNKNOWN_HMAC;
+    goto err;
+  }
+
   /* Encode the payload */
   status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
   if (status != SILC_SKE_STATUS_OK)
@@ -524,6 +539,8 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
     silc_cipher_free(prop->cipher);
   if (prop->hash)
     silc_hash_free(prop->hash);
+  if (prop->hmac)
+    silc_hmac_free(prop->hmac);
   silc_free(prop);
   ske->prop = NULL;
 
@@ -791,6 +808,10 @@ silc_ske_assemble_security_properties(SilcSKE ske,
   rp->hash_alg_list = silc_hash_get_supported();
   rp->hash_alg_len = strlen(rp->hash_alg_list);
 
+  /* Get supported HMACs */
+  rp->hmac_alg_list = silc_hmac_get_supported();
+  rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+
   /* XXX */
   /* Get supported compression algorithms */
   rp->comp_alg_list = "";
@@ -800,7 +821,7 @@ silc_ske_assemble_security_properties(SilcSKE ske,
     2 + rp->version_len +
     2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len + 
     2 + rp->enc_alg_len + 2 + rp->hash_alg_len + 
-    2 + rp->comp_alg_len;
+    2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
 
   *return_payload = rp;
 
@@ -1056,6 +1077,64 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->hash_alg_list = strdup(rp->hash_alg_list);
   }
 
+  /* Get supported HMACs */
+  cp = rp->hmac_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
+
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      memcpy(item, cp, len);
+
+      SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
+
+      if (silc_hmac_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found HMAC `%s'", item));
+
+       payload->hmac_alg_len = len;
+       payload->hmac_alg_list = item;
+       break;
+      }
+
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
+
+      if (item)
+       silc_free(item);
+    }
+
+    if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
+      SILC_LOG_DEBUG(("Could not find supported HMAC"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload->pkcs_alg_list);
+      silc_free(payload->enc_alg_list);
+      silc_free(payload->hash_alg_list);
+      silc_free(payload);
+      return SILC_SKE_STATUS_UNKNOWN_HMAC;
+    }
+  } else {
+
+    if (!rp->hmac_alg_len) {
+      SILC_LOG_DEBUG(("HMAC not defined in payload"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload->pkcs_alg_list);
+      silc_free(payload->enc_alg_list);
+      silc_free(payload->hash_alg_list);
+      silc_free(payload);
+      return SILC_SKE_STATUS_BAD_PAYLOAD;
+    }
+
+    SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
+                   rp->hmac_alg_list));
+
+    payload->hmac_alg_len = rp->hmac_alg_len;
+    payload->hmac_alg_list = strdup(rp->hmac_alg_list);
+  }
+
 #if 0
   /* Get supported compression algorithms */
   cp = rp->hash_alg_list;
@@ -1105,7 +1184,7 @@ silc_ske_select_security_properties(SilcSKE ske,
     2 + payload->version_len + 
     2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len + 
     2 + payload->enc_alg_len + 2 + payload->hash_alg_len + 
-    2 + payload->comp_alg_len;
+    2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
 
   return SILC_SKE_STATUS_OK;
 }
@@ -1271,6 +1350,7 @@ silc_ske_process_key_material_data(unsigned char *data,
     
     /* Take third round */
     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull_tail(dist, hash_len);
     silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
@@ -1330,6 +1410,7 @@ silc_ske_process_key_material_data(unsigned char *data,
     
     /* Take third round */
     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull_tail(dist, hash_len);
     silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
@@ -1417,6 +1498,9 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
 
 void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
 {
+  if (!key)
+    return;
+
   if (key->send_iv)
     silc_free(key->send_iv);
   if (key->receive_iv)
index 1a2bdc28cc1dbedc64f61f01ed300368bb182695..880475e028f2183eed0fc4cbd835f576c2435d84 100644 (file)
@@ -92,7 +92,8 @@ struct SilcSKESecurityPropertiesStruct {
   SilcPKCS pkcs;
   SilcCipher cipher;
   SilcHash hash;
-  /* XXX SilcCompression comp; */
+  SilcHmac hmac;
+  /* XXX SilcZip comp; */
 };
 
 struct SilcSKEStruct {
index d81ed54cd8c586f18f398348f5802260f68344aa..f6ef92128c02d1ab6d694330f88750c261d013e8 100644 (file)
@@ -31,9 +31,10 @@ typedef enum {
   SILC_SKE_STATUS_UNKNOWN_CIPHER         = 4,
   SILC_SKE_STATUS_UNKNOWN_PKCS           = 5,
   SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION  = 6,
-  SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY = 7,
-  SILC_SKE_STATUS_INCORRECT_SIGNATURE    = 8,
-  SILC_SKE_STATUS_BAD_VERSION            = 9,
+  SILC_SKE_STATUS_UNKNOWN_HMAC           = 7,
+  SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY = 8,
+  SILC_SKE_STATUS_INCORRECT_SIGNATURE    = 9,
+  SILC_SKE_STATUS_BAD_VERSION            = 10,
 
   SILC_SKE_STATUS_KEY_EXCHANGE_NOT_ACTIVE,
   SILC_SKE_STATUS_BAD_RESERVED_FIELD,
index 7faae5463aad65cd77a13da2b03031cdf2eaec99..ae5a27aecd8f65abb1ff56608de55f6666b5e4c4 100644 (file)
@@ -145,6 +145,9 @@ int silc_buffer_format(SilcBuffer dst, ...)
 
  fail:
   SILC_LOG_DEBUG(("Error occured while formatting data"));
+#ifdef SILC_DEBUG
+  assert(FALSE);
+#endif
   return -1;
 
  ok:
@@ -367,6 +370,9 @@ int silc_buffer_unformat(SilcBuffer src, ...)
 
  fail:
   SILC_LOG_DEBUG(("Error occured while unformatting buffer"));
+#ifdef SILC_DEBUG
+  assert(FALSE);
+#endif
   return -1;
 
  ok:
index 152487e48e6bf82533e060b63db6671f16fb63a6..19c17f37dbddadfe0e1af00240e407debec1d885 100644 (file)
@@ -571,6 +571,29 @@ void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
   }
 }
 
+/* Unregister a task by callback function. This invalidates the task. */
+
+void silc_task_unregister_by_callback(SilcTaskQueue queue, 
+                                     SilcTaskCallback callback)
+{
+  SilcTask next;
+
+  SILC_LOG_DEBUG(("Unregister task by callback"));
+
+  if (queue->task == NULL)
+    return;
+
+  next = queue->task;
+
+  while(1) {
+    if (next->callback == callback)
+      next->valid = FALSE;
+    if (queue->task == next->next)
+      break;
+    next = next->next;
+  }
+}
+
 /* Sets the I/O mask for the task. Only one I/O type can be set at a
    time. */
 
index cb353e4f385ef8430efc23752f3a9b1ef2327df6..13a3ec90b3669d1abf7aa62e39bdab6eec495206 100644 (file)
@@ -331,6 +331,8 @@ typedef SilcTaskQueueObject *SilcTaskQueue;
    with name 'func' as a task callback function. */
 #define SILC_TASK_CALLBACK(func) \
 static void func(void *qptr, int type, void *context, int fd)
+#define SILC_TASK_CALLBACK_GLOBAL(func) \
+void func(void *qptr, int type, void *context, int fd)
 
 /* Prototypes */
 void silc_task_queue_alloc(SilcTaskQueue *new, int valid);
@@ -347,6 +349,8 @@ SilcTask silc_task_register(SilcTaskQueue queue, int fd,
 int silc_task_remove(SilcTaskQueue queue, SilcTask task);
 void silc_task_unregister(SilcTaskQueue queue, SilcTask task);
 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd);
+void silc_task_unregister_by_callback(SilcTaskQueue queue, 
+                                     SilcTaskCallback callback);
 void silc_task_set_iotype(SilcTask task, int type);
 void silc_task_reset_iotype(SilcTask task, int type);
 int silc_task_timeout_compare(struct timeval *smaller,