updates.
authorPekka Riikonen <priikone@silcnet.org>
Wed, 28 Mar 2001 16:20:00 +0000 (16:20 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 28 Mar 2001 16:20:00 +0000 (16:20 +0000)
19 files changed:
CHANGES
README
TODO
apps/silc/client_ops.c
apps/silc/clientutil.c
apps/silc/local_command.c
apps/silc/local_command.h
apps/silcd/testi2.conf
lib/silcclient/README
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_keyagr.c
lib/silcclient/client_prvmsg.c
lib/silcclient/idlist.c
lib/silcclient/protocol.c
lib/silcclient/silcapi.h
lib/silccore/silcpacket.c
lib/silcutil/silcnet.c
lib/silcutil/silcnet.h

diff --git a/CHANGES b/CHANGES
index 0ffcf53f546f2e0ae1a70fa5a5067c1b3038ce5f..6699e53be784dd5a7e46ee10bc910a75039ee151 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,38 @@
+Wed Mar 28 15:52:36 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SilcKeyAgreementStatus type to the key agreement routines
+         to indicate the current status and error if one occured.
+         The status types are defined in the lib/silcclient/silcapi.h.
+
+       * Added new local command KEY that is used to set and unset private
+         keys for channels, set and unset private keys for private messages
+         with remote clients and to send key agreement requests and
+         negotiate the key agreement protocol with remote client.  The
+         key agreement is supported only to negotiate private message keys,
+         it currently cannot be used to negotiate private keys for channels,
+         as it is not convenient for that purpose.
+
+       * Fixed a minor pending callback setting bug in the function
+         silc_client_get_client_by_id_resolve, now the function works.
+         Affected file lib/silcclient/idlist.c.
+
+       * Added function silc_net_get_local_port to get local bound
+         port by socket.  Added to lib/silcutil/silcnet.[ch].
+
+       * Added `sockets' and `sockets_count' fields to the SilcClient
+         object.  They hold the sockets of the listenning sockets in
+         the client.  Listenning sockets may be for example the key 
+         agreement server.  Affected file lib/silcclient/client.[ch].
+         Added functions the silc_client_add_socket and the
+         silc_client_del_socket.  They are exported to the application
+         as well.
+
+       * Added ~./silc/clientkeys to support other client's public keys.
+
+       * Renamed verify_server_key client operation to verify_public_key
+         and added one argument to indicate the type of the connection
+         (server, client etc.).
+
 Tue Mar 27 22:22:38 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Added silc_server_connection_auth_request to handle the
diff --git a/README b/README
index 3a339ef3ae5335c0b4f0b1b81c784acc9721cea7..6c0808f1da93443c2d6dcbef10ecb52053d681c4 100644 (file)
--- a/README
+++ b/README
@@ -230,6 +230,91 @@ SILC Commands
                specified.  If the channel cannot be found then all
                channels are listed.
 
+       /KEY    msg|channel <nickname|channel> 
+               set|unset|list|agreement|negotiate [<arguments>]
+
+               This command is used to set and unset private keys for
+               channels, set and unset private keys for private messages
+               with remote clients and to send key agreement requests and
+               negotiate the key agreement protocol with remote client.
+               The key agreement is supported only to negotiate private
+               message keys, it currently cannot be used to negotiate
+               private keys for channels, as it is not convenient for that
+               purpose.
+
+               Types:          
+
+               msg     The command is performed for private messages
+                       affecting the <nickname>.
+
+               channel The command is performed for channel affecting
+                       the <channel>.
+
+
+               Commands:
+
+               set     [<key> [<cipher>] [<hmac>]]
+
+                       Set the key into use.  If the <key> is provided it
+                       is used as the key material.  If the <key> is not
+                       provided the negotiated key material is used.  If
+                       the negotiation has not been performed this command
+                       has no effect.
+
+                       If the type is `msg' and the <key> is `*' then
+                       random key will be generated automatically.
+
+                       The <cipher> may be set for both private message
+                       and channel private keys and the <hmac> may be set
+                       only to the channel private keys.
+
+               unset   [<number>]
+
+                       Unset the key.  The private key is not used after
+                       this command.  The key must be set again or the key
+                       material must be re-negotiated to be able to use
+                       the private keys again.
+
+                       The channel may have several private keys set.  The
+                       <number> can be used to indicate what key is being
+                       unset.  If it is not provided all keys are removed.
+
+
+               list    List all private keys that has been set.
+
+                       If the type is `msg' and the <nickname> is ยด*' then
+                       all private message keys that you've set will be
+                       listed.
+
+               agreement [<hostname> [<port>]]
+
+                       Send key agreement request to remote client.  If
+                       the <hostname> is provided it is sent in the request.
+                       The receiver may use the hostname to start the
+                       key agreement.  If the <port> is also provided your
+                       key agreement protocol server is bound to that
+                       port.  Note that it cannot be privileged port (<1023).
+                       If the <hostname> and <port> is not provided then
+                       the receiver will never initiate the key agreement.
+                       In this case you must start the key agreement after
+                       receiving the reply to the request, by giving the
+                       /KEYAGR start command.
+
+                       This command may be used to send reply to the
+                       remote client.  When receiving empty key agreement
+                       you can reply to the sender with the hostname and
+                       port of your key agreement server with this command.
+
+               negotiate [<hostname> [<port>]]
+
+                       This may be called to start the key agreement with
+                       <nickname>.  This command has effect only if the
+                       <nickname> has replied to your key agreement request.
+                       You will see a notify on the screen when the reply
+                       arrives.  The <hostname> and <port> is the hostname
+                       and port of the remote client's key agreement
+                       server.
+
 
 Features
 ========
diff --git a/TODO b/TODO
index 2994a564fdc3bb6ec9dbc92e5e84c9540c2a46fb..ee740ec963d9196932a395f19e0431c26498e13e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -25,12 +25,9 @@ TODO General
 TODO In SILC Client Library
 ===========================
 
- o TODO in commands (silc/local_command.c, lib/silcclient/command.c and
+ o TODO in commands (lib/silcclient/command.c and
                     silc/silclient/command_reply.c):
 
-       o Local command to handle private message keys is not done
-       o Local command to handle channel private keys is not done
-       o Local command to handle key agreement protocol is not done
        o RESTART command is not implemented
 
  o Client library crashes if for example server timeouts protocol
@@ -78,6 +75,13 @@ TODO In SILC Server
          When implementing this check that all commands handle the
          situation correctly when it is called as pending command
          (it should most likely check that cmd->pending == TRUE/FALSE).
+       o WHOIS and IDENTIFY command reply sending is buggy because
+         it may put status to be SILC_STATUS_LIST_START even though
+         there is only one valid entry (thus should be SILC_STATUS_OK).
+         This happens because it does not check the data.registered == FALSE
+         clients before setting the status.  Good fix for this would be
+         to make sure that the clients table does not include any
+         unregistered clients in the first place.
 
  o TODO in notify types (packet_receive.c):
 
index 5be53ad958435f60b9e8498cf2d7db084ad25de6..19d675b266087731d6a288c95b364b7a39dd37c7 100644 (file)
@@ -841,11 +841,12 @@ unsigned char *silc_ask_passphrase(SilcClient client,
 }
 
 /* Verifies received public key. If user decides to trust the key it is
-   saved as trusted server key for later use. If user does not trust the
+   saved as public server key for later use. If user does not trust the
    key this returns FALSE. */
 
-int silc_verify_server_key(SilcClient client,
+int silc_verify_public_key(SilcClient client,
                           SilcClientConnection conn, 
+                          SilcSocketType conn_type,
                           unsigned char *pk, unsigned int pk_len,
                           SilcSKEPKType pk_type)
 {
@@ -855,11 +856,15 @@ int silc_verify_server_key(SilcClient client,
   char *hostname, *fingerprint;
   struct passwd *pw;
   struct stat st;
+  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
+                 "server" : "client");
 
   hostname = sock->hostname ? sock->hostname : sock->ip;
 
   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
-    silc_say(client, conn, "We don't support server %s key type", hostname);
+    silc_say(client, conn, "We don't support %s %s key type", 
+            entity, hostname);
     return FALSE;
   }
 
@@ -869,17 +874,18 @@ int silc_verify_server_key(SilcClient client,
 
   memset(filename, 0, sizeof(filename));
   memset(file, 0, sizeof(file));
-  snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
+  snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, hostname,
           sock->port);
-  snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
-          pw->pw_dir, file);
+  snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+          pw->pw_dir, entity, file);
 
   /* Check wheter this key already exists */
   if (stat(filename, &st) < 0) {
 
     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-    silc_say(client, conn, "Received server %s public key", hostname);
-    silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+    silc_say(client, conn, "Received %s %s public key", entity, hostname);
+    silc_say(client, conn, "Fingerprint for the %s %s key is", entity, 
+            hostname);
     silc_say(client, conn, "%s", fingerprint);
     silc_free(fingerprint);
 
@@ -904,12 +910,13 @@ int silc_verify_server_key(SilcClient client,
       if (!silc_pkcs_load_public_key(filename, &public_key, 
                                     SILC_PKCS_FILE_BIN)) {
        fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-       silc_say(client, conn, "Received server %s public key", hostname);
-       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+       silc_say(client, conn, "Received %s %s public key", entity, hostname);
+       silc_say(client, conn, "Fingerprint for the %s %s key is", 
+                entity, hostname);
        silc_say(client, conn, "%s", fingerprint);
        silc_free(fingerprint);
-       silc_say(client, conn, "Could not load your local copy of the server %s key",
-                hostname);
+       silc_say(client, conn, "Could not load your local copy of the %s %s key",
+                entity, hostname);
        if (silc_client_ask_yes_no(client, 
           "Would you like to accept the key anyway (y/n)? "))
          {
@@ -927,12 +934,13 @@ int silc_verify_server_key(SilcClient client,
     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
     if (!encpk) {
       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-      silc_say(client, conn, "Received server %s public key", hostname);
-      silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+      silc_say(client, conn, "Received %s %s public key", entity, hostname);
+      silc_say(client, conn, "Fingerprint for the %s %s key is", 
+              entity, hostname);
       silc_say(client, conn, "%s", fingerprint);
       silc_free(fingerprint);
-      silc_say(client, conn, "Your local copy of the server %s key is malformed",
-              hostname);
+      silc_say(client, conn, "Your local copy of the %s %s key is malformed",
+              entity, hostname);
       if (silc_client_ask_yes_no(client, 
          "Would you like to accept the key anyway (y/n)? "))
        {
@@ -948,12 +956,13 @@ int silc_verify_server_key(SilcClient client,
 
     if (memcmp(encpk, pk, encpk_len)) {
       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-      silc_say(client, conn, "Received server %s public key", hostname);
-      silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+      silc_say(client, conn, "Received %s %s public key", entity, hostname);
+      silc_say(client, conn, "Fingerprint for the %s %s key is", 
+              entity, hostname);
       silc_say(client, conn, "%s", fingerprint);
       silc_free(fingerprint);
-      silc_say(client, conn, "Server %s key does not match with your local copy",
-              hostname);
+      silc_say(client, conn, "%s %s key does not match with your local copy",
+              entity, hostname);
       silc_say(client, conn, "It is possible that the key has expired or changed");
       silc_say(client, conn, "It is also possible that some one is performing "
                       "man-in-the-middle attack");
@@ -969,7 +978,7 @@ int silc_verify_server_key(SilcClient client,
          return TRUE;
        }
 
-      silc_say(client, conn, "Will not accept server %s key", hostname);
+      silc_say(client, conn, "Will not accept %s %s key", entity, hostname);
       return FALSE;
     }
 
@@ -977,7 +986,7 @@ int silc_verify_server_key(SilcClient client,
     return TRUE;
   }
 
-  silc_say(client, conn, "Will not accept server %s key", hostname);
+  silc_say(client, conn, "Will not accept %s %s key", entity, hostname);
   return FALSE;
 }
 
@@ -1048,6 +1057,21 @@ int silc_key_agreement(SilcClient client, SilcClientConnection conn,
                       SilcKeyAgreementCallback *completion,
                       void **context)
 {
+  char host[256];
+
+  /* We will just display the info on the screen and return FALSE and user
+     will have to start the key agreement with a command. */
+
+  if (hostname) {
+    memset(host, 0, sizeof(host));
+    snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
+  }
+
+  silc_say(client, conn, "%s wants to perform key agreement %s",
+          client_entry->nickname, hostname ? host : "");
+
+  *completion = NULL;
+  *context = NULL;
 
   return FALSE;
 }
@@ -1063,7 +1087,7 @@ SilcClientOperations ops = {
   connect:              silc_connect,
   disconnect:           silc_disconnect,
   get_auth_method:      silc_get_auth_method,
-  verify_server_key:    silc_verify_server_key,
+  verify_public_key:    silc_verify_public_key,
   ask_passphrase:       silc_ask_passphrase,
   failure:              silc_failure,
   key_agreement:        silc_key_agreement,
index 22e84bdce37a2bee566488aff87d29d1962a2725..6834916a62bcbde683a1cc52c00c0000fd64674f 100644 (file)
@@ -506,7 +506,7 @@ New pair of keys will be created.  Please, answer to following questions.\n\
 int silc_client_check_silc_dir()
 {
   char filename[256], file_public_key[256], file_private_key[256];
-  char servfilename[256];
+  char servfilename[256], clientfilename[256];
   char *identifier;
   struct stat st;
   struct passwd *pw;
@@ -531,6 +531,8 @@ int silc_client_check_silc_dir()
   snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
   snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
           pw->pw_dir);
+  snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/.silc/clientkeys", 
+          pw->pw_dir);
 
   /*
    * Check ~/.silc directory
@@ -596,6 +598,28 @@ int silc_client_check_silc_dir()
     }
   }
   
+  /*
+   * Check ~./silc/clientkeys directory
+   */
+  if ((stat(clientfilename, &st)) == -1) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {
+      if (pw->pw_uid == geteuid()) {
+       if ((mkdir(clientfilename, 0755)) == -1) {
+         fprintf(stderr, "Couldn't create `%s' directory\n", clientfilename);
+         return FALSE;
+       }
+      } else {
+       fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+               clientfilename);
+       return FALSE;
+      }
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+  
   /*
    * Check Public and Private keys
    */
index 0329366511ba39307c57521ec904127113e653b4..1938e9f9d2832cc74453736b3e320f07441bcfc1 100644 (file)
@@ -31,6 +31,7 @@ SilcClientCommand silc_local_command_list[] =
   SILC_CLIENT_LCMD(server, SERVER, "SERVER", 0, 2),
   SILC_CLIENT_LCMD(msg, MSG, "MSG", 0, 3),
   SILC_CLIENT_LCMD(away, AWAY, "AWAY", 0, 2),
+  SILC_CLIENT_LCMD(key, KEY, "KEY", 0, 7),
 
   { NULL, 0, NULL, 0, 0 },
 };
@@ -62,13 +63,6 @@ SILC_CLIENT_LCMD_FUNC(help)
 SILC_CLIENT_LCMD_FUNC(clear)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClient client = cmd->client;
-  SilcClientInternal app = (SilcClientInternal)client->application;
-
-#if 0
-  wclear((WINDOW *)app->screen);
-  wrefresh((WINDOW *)app->screen);
-#endif
 
   silc_client_command_free(cmd);
 }
@@ -256,3 +250,444 @@ SILC_CLIENT_LCMD_FUNC(away)
  out:
   silc_client_command_free(cmd);
 }
+
+typedef struct {
+  int type;                    /* 1 = msg, 2 = channel */
+} *KeyInternal;
+
+static SilcSKEKeyMaterial *curr_key = NULL;
+
+/* 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. */
+
+static void keyagr_completion(SilcClient client,
+                             SilcClientConnection conn,
+                             SilcClientEntry client_entry,
+                             SilcKeyAgreementStatus status,
+                             SilcSKEKeyMaterial *key,
+                             void *context)
+{
+  KeyInternal i = (KeyInternal)context;
+
+  curr_key = NULL;
+
+  switch(status) {
+  case SILC_KEY_AGREEMENT_OK:
+    silc_say(client, conn, "Key agreement compeleted successfully with %s",
+            client_entry->nickname);;
+
+    if (i->type == 1) {
+      if (!silc_client_ask_yes_no(client, 
+         "Would you like to use the key with private messages (y/n)? ")) {
+       silc_say(client, conn, "You can set the key material into use later by giving /KEY msg set command");
+       curr_key = key;
+       break;
+      }
+      
+      /* Set the private key for this client */
+      silc_client_del_private_message_key(client, conn, client_entry);
+      silc_client_add_private_message_key_ske(client, conn, client_entry,
+                                             NULL, key);
+      silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
+      silc_ske_free_key_material(key);
+    }
+    
+    break;
+    
+  case SILC_KEY_AGREEMENT_ERROR:
+    silc_say(client, conn, "Error occured during key agreement with %s",
+            client_entry->nickname);
+    break;
+    
+  case SILC_KEY_AGREEMENT_FAILURE:
+    silc_say(client, conn, "The key agreement failed with %s",
+            client_entry->nickname);
+    break;
+    
+  case SILC_KEY_AGREEMENT_TIMEOUT:
+    silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
+            client_entry->nickname);
+    break;
+    
+  default:
+    break;
+  } 
+
+  if (i)
+    silc_free(i);
+}
+
+/* Local command KEY. This command is used to set and unset private
+   keys for channels, set and unset private keys for private messages
+   with remote clients and to send key agreement requests and
+   negotiate the key agreement protocol with remote client.  The
+   key agreement is supported only to negotiate private message keys,
+   it currently cannot be used to negotiate private keys for channels,
+   as it is not convenient for that purpose. */
+
+SILC_CLIENT_LCMD_FUNC(key)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = cmd->client;
+  SilcClientEntry client_entry = NULL;
+  SilcChannelEntry channel_entry = NULL;
+  unsigned int num = 0;
+  char *nickname = NULL, *server = NULL;
+  int command = 0, port = 0, type = 0;
+  char *hostname = NULL;
+  KeyInternal internal = NULL;
+
+  if (!cmd->conn) {
+    silc_say(client, conn,
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  if (cmd->argc < 4) {
+    silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+            "set|unset|agreement|negotiate [<arguments>]");
+    goto out;
+  }
+
+  /* Get type */
+  if (!strcasecmp(cmd->argv[1], "msg"))
+    type = 1;
+  if (!strcasecmp(cmd->argv[1], "channel"))
+    type = 2;
+
+  if (type == 0) {
+    silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+            "set|unset|agreement|negotiate [<arguments>]");
+    goto out;
+  }
+
+  if (type == 1) {
+    if (cmd->argv[2][0] == '*') {
+      nickname = "*";
+    } else {
+      /* Parse the typed nickname. */
+      if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
+       silc_say(client, conn, "Bad nickname");
+       goto out;
+      }
+      
+      /* Find client entry */
+      client_entry = silc_idlist_get_client(client, conn, nickname, 
+                                           server, num, TRUE);
+      if (!client_entry) {
+       /* Client entry not found, it was requested thus mark this to be
+          pending command. */
+       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                   conn->cmd_ident, 
+                                   NULL, silc_client_local_command_key, 
+                                   context);
+       return;
+      }
+    }
+  }
+
+  if (type == 2) {
+    /* Get channel entry */
+    char *name;
+
+    if (cmd->argv[2][0] == '*') {
+      if (!conn->current_channel) {
+       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+       goto out;
+      }
+      name = conn->current_channel->channel_name;
+    } else {
+      name = cmd->argv[2];
+    }
+
+    channel_entry = silc_client_get_channel(client, conn, name);
+    if (!channel_entry) {
+      silc_say(client, conn, "You are not on that channel");
+      goto out;
+    }
+  }
+
+  /* Set command */
+  if (!strcasecmp(cmd->argv[3], "set")) {
+    command = 1;
+
+    if (cmd->argc == 3) {
+      if (curr_key && type == 1 && client_entry) {
+       silc_client_del_private_message_key(client, conn, client_entry);
+       silc_client_add_private_message_key_ske(client, conn, client_entry,
+                                               NULL, curr_key);
+       goto out;
+      }
+    }
+
+    if (cmd->argc >= 4) {
+      if (type == 1 && client_entry) {
+       /* Set private message key */
+       
+       silc_client_del_private_message_key(client, conn, client_entry);
+
+       if (cmd->argc >= 5)
+         silc_client_add_private_message_key(client, conn, client_entry,
+                                             cmd->argv[5], cmd->argv[4],
+                                             cmd->argv_lens[4],
+                                             (cmd->argv[4][0] == '*' ?
+                                              TRUE : FALSE));
+       else
+         silc_client_add_private_message_key(client, conn, client_entry,
+                                             NULL, cmd->argv[4],
+                                             cmd->argv_lens[4],
+                                             (cmd->argv[4][0] == '*' ?
+                                              TRUE : FALSE));
+
+       /* Send the key to the remote client so that it starts using it
+          too. */
+       silc_client_send_private_message_key(client, conn, client_entry, TRUE);
+      } else if (type == 2) {
+       /* Set private channel key */
+       char *cipher = NULL, *hmac = NULL;
+
+       if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+         silc_say(client, conn, 
+                  "Private key mode is not set on this channel");
+         goto out;
+       }
+
+       if (cmd->argc >= 5)
+         cipher = cmd->argv[5];
+       if (cmd->argc >= 6)
+         hmac = cmd->argv[6];
+
+       if (!silc_client_add_channel_private_key(client, conn, channel_entry,
+                                                cipher, hmac,
+                                                cmd->argv[4],
+                                                cmd->argv_lens[4])) {
+         silc_say(client, conn, "Could not add channel private key");
+         goto out;
+       }
+      }
+    }
+
+    goto out;
+  }
+  
+  /* Unset command */
+  if (!strcasecmp(cmd->argv[3], "unset")) {
+    command = 2;
+
+    if (type == 1 && client_entry) {
+      /* Unset private message key */
+      silc_client_del_private_message_key(client, conn, client_entry);
+    } else if (type == 2) {
+      /* Unset channel key(s) */
+      SilcChannelPrivateKey *keys;
+      unsigned int keys_count;
+      int number;
+
+      if (cmd->argc == 3)
+       silc_client_del_channel_private_keys(client, conn, channel_entry);
+
+      if (cmd->argc > 3) {
+       number = atoi(cmd->argv[4]);
+       keys = silc_client_list_channel_private_keys(client, conn, 
+                                                    channel_entry,
+                                                    &keys_count);
+       if (!keys)
+         goto out;
+
+       if (!number || number > keys_count) {
+         silc_client_free_channel_private_keys(keys, keys_count);
+         goto out;
+       }
+
+       silc_client_del_channel_private_key(client, conn, channel_entry,
+                                           keys[number - 1]);
+       silc_client_free_channel_private_keys(keys, keys_count);
+      }
+
+      goto out;
+    }
+  }
+
+  /* List command */
+  if (!strcasecmp(cmd->argv[3], "list")) {
+    command = 3;
+
+    if (type == 1) {
+      SilcPrivateMessageKeys keys;
+      unsigned int keys_count;
+      int k, i, len;
+      char buf[1024];
+
+      keys = silc_client_list_private_message_keys(client, conn, 
+                                                  &keys_count);
+      if (!keys)
+       goto out;
+
+      /* list the private message key(s) */
+      if (nickname[0] == '*') {
+       silc_say(client, conn, "Private message keys");
+       silc_say(client, conn, 
+                "  Client                        Cipher        Key");
+       for (k = 0; k < keys_count; k++) {
+         memset(buf, 0, sizeof(buf));
+         strncat(buf, "  ", 2);
+         len = strlen(keys[k].client_entry->nickname);
+         strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+         if (len < 30)
+           for (i = 0; i < 30 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+         
+         len = strlen(keys[k].cipher);
+         strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+         if (len < 14)
+           for (i = 0; i < 14 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+
+         if (keys[k].key)
+           strcat(buf, "<hidden>");
+         else
+           strcat(buf, "*generated*");
+
+         silc_say(client, conn, "%s", buf);
+       }
+      } else {
+       silc_say(client, conn, "Private message key", 
+                client_entry->nickname);
+       silc_say(client, conn, 
+                "  Client                        Cipher        Key");
+       for (k = 0; k < keys_count; k++) {
+         if (keys[k].client_entry != client_entry)
+           continue;
+
+         memset(buf, 0, sizeof(buf));
+         strncat(buf, "  ", 2);
+         len = strlen(keys[k].client_entry->nickname);
+         strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+         if (len < 30)
+           for (i = 0; i < 30 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+         
+         len = strlen(keys[k].cipher);
+         strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+         if (len < 14)
+           for (i = 0; i < 14 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+
+         if (keys[k].key)
+           strcat(buf, "<hidden>");
+         else
+           strcat(buf, "*generated*");
+
+         silc_say(client, conn, "%s", buf);
+       }
+      }
+
+      silc_client_free_private_message_keys(keys, keys_count);
+    } else if (type == 2) {
+      SilcChannelPrivateKey *keys;
+      unsigned int keys_count;
+      int k, i, len;
+      char buf[1024];
+
+      keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
+                                                  &keys_count);
+      if (!keys)
+       goto out;
+
+      silc_say(client, conn, "Channel %s private keys", 
+              channel_entry->channel_name);
+      silc_say(client, conn, 
+              "  Cipher          Hmac            Key");
+      for (k = 0; k < keys_count; k++) {
+       memset(buf, 0, sizeof(buf));
+       strncat(buf, "  ", 2);
+
+       len = strlen(keys[k]->cipher->cipher->name);
+       strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
+       if (len < 16)
+         for (i = 0; i < 16 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+       
+       len = strlen(keys[k]->hmac->hmac->name);
+       strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
+       if (len < 16)
+         for (i = 0; i < 16 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+       
+       strcat(buf, "<hidden>");
+
+       silc_say(client, conn, "%s", buf);
+      }
+      
+      silc_client_free_channel_private_keys(keys, keys_count);
+    }
+
+    goto out;
+  }
+
+  /* Send command is used to send key agreement */
+  if (!strcasecmp(cmd->argv[3], "agreement")) {
+    command = 4;
+
+    if (cmd->argc >= 5)
+      hostname = cmd->argv[4];
+    if (cmd->argc >= 6)
+      port = atoi(cmd->argv[5]);
+
+    internal = silc_calloc(1, sizeof(*internal));
+    internal->type = type;
+  }
+
+  /* Start command is used to start key agreement (after receiving the
+     key_agreement client operation). */
+  if (!strcasecmp(cmd->argv[3], "negotiate")) {
+    command = 5;
+
+    if (cmd->argc >= 5)
+      hostname = cmd->argv[4];
+    if (cmd->argc >= 6)
+      port = atoi(cmd->argv[5]);
+
+    internal = silc_calloc(1, sizeof(*internal));
+    internal->type = type;
+  }
+
+  if (command == 0) {
+    silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+            "set|unset|agreement|negotiate [<arguments>]");
+    goto out;
+  }
+
+  if (command == 4 && client_entry) {
+    silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
+    silc_client_send_key_agreement(client, conn, client_entry, hostname, 
+                                  port, 120, keyagr_completion, internal);
+    goto out;
+  }
+
+  if (command == 5 && client_entry) {
+    silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
+    silc_client_perform_key_agreement(client, conn, client_entry, hostname, 
+                                     port, keyagr_completion, internal);
+    goto out;
+  }
+
+ out:
+  if (nickname)
+    silc_free(nickname);
+  if (server)
+    silc_free(server);
+  silc_client_command_free(cmd);
+}
index 56730578e734a927aad878caecf2842ce740e843..bd61ae9c8feb9ea01c1ed4e96a1ded9ef76b8562 100644 (file)
@@ -31,6 +31,7 @@ extern SilcClientCommand silc_local_command_list[];
 #define SILC_LOCAL_COMMAND_SERVER       4
 #define SILC_LOCAL_COMMAND_MSG                 5
 #define SILC_LOCAL_COMMAND_AWAY                6
+#define SILC_LOCAL_COMMAND_KEY         7
 
 /* Macros */
 
@@ -51,5 +52,6 @@ SILC_CLIENT_LCMD_FUNC(version);
 SILC_CLIENT_LCMD_FUNC(msg);
 SILC_CLIENT_LCMD_FUNC(server);
 SILC_CLIENT_LCMD_FUNC(away);
+SILC_CLIENT_LCMD_FUNC(key);
 
 #endif
index 42ed3eae557fcc11cf459bdcbb77c8bacb6813ad..0dc9377a1c3efa0800e8a1616305127c0dabd14f 100644 (file)
@@ -56,7 +56,6 @@ errorlogfile:silcd2.log:10000
 10.2.1.7:passwd:priikone:1335:1:1:0
 
 [DenyConnection]
-[RedirectClient]
 
 [motd]
 ./motd
index 85748c280e6cd16a68da9692b2cdcbb65eb982f9..0fb6e0b40b3bcd487884f5a802f7ec8f0f91488c 100644 (file)
@@ -51,7 +51,7 @@ SilcClientOperations ops = {
   connect:              silc_connect,
   disconnect:           silc_disconnect,
   get_auth_method:      silc_get_auth_method,
-  verify_server_key:    silc_verify_server_key,
+  verify_public_key:    silc_verify_public_key,
   ask_passphrase:       silc_ask_passphrase,
   failure:              silc_failure,
 };
index 089ea87aeee92e53eecb29c5ac529052691f9843..7637466b0624e4409986bb9e4441f9fa880d7dc5 100644 (file)
@@ -179,6 +179,51 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
     }
 }
 
+/* Adds listener socket to the listener sockets table. This function is
+   used to add socket objects that are listeners to the client.  This should
+   not be used to add other connection objects. */
+
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->sockets) {
+    client->sockets = silc_calloc(1, sizeof(*client->sockets));
+    client->sockets[0] = sock;
+    client->sockets_count = 1;
+    return;
+  }
+
+  for (i = 0; i < client->sockets_count; i++) {
+    if (client->sockets[i] == NULL) {
+      client->sockets[i] = sock;
+      return;
+    }
+  }
+
+  client->sockets = silc_realloc(client->sockets, sizeof(*client->sockets) *
+                                (client->sockets_count + 1));
+  client->sockets[client->sockets_count] = sock;
+  client->sockets_count++;
+}
+
+/* Deletes listener socket from the listener sockets table. */
+
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->sockets)
+    return;
+
+  for (i = 0; i < client->sockets_count; i++) {
+    if (client->sockets[i] == sock) {
+      client->sockets[i] = NULL;
+      return;
+    }
+  }
+}
+
 static int 
 silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
 {
@@ -621,8 +666,12 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 
     /* Process the packet. This will call the parser that will then
        decrypt and parse the packet. */
-    silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
-                               silc_client_packet_parse, client);
+    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+      silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
+                                 silc_client_packet_parse, client);
+    else
+      silc_packet_receive_process(sock, NULL, NULL,
+                                 silc_client_packet_parse, client);
   }
 }
 
@@ -666,8 +715,13 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
   SILC_LOG_DEBUG(("Start"));
 
   /* Decrypt the received packet */
-  ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
-                           silc_client_packet_decrypt_check, parse_ctx);
+  if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+    ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
+                             silc_client_packet_decrypt_check, parse_ctx);
+  else
+    ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
+                             silc_client_packet_decrypt_check, parse_ctx);
+
   if (ret < 0)
     goto out;
 
@@ -795,7 +849,8 @@ void silc_client_packet_parse_type(SilcClient client,
     break;
 
   case SILC_PACKET_KEY_EXCHANGE:
-    if (sock->protocol) {
+    if (sock->protocol && sock->protocol->protocol->type 
+       == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
       SilcClientKEInternalContext *proto_ctx = 
        (SilcClientKEInternalContext *)sock->protocol->context;
 
@@ -818,15 +873,32 @@ void silc_client_packet_parse_type(SilcClient client,
     break;
 
   case SILC_PACKET_KEY_EXCHANGE_1:
-    if (sock->protocol) {
+    if (sock->protocol && sock->protocol->protocol->type 
+       == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+      SilcClientKEInternalContext *proto_ctx = 
+       (SilcClientKEInternalContext *)sock->protocol->context;
 
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
+
+      proto_ctx->packet = silc_packet_context_dup(packet);
+      proto_ctx->dest_id_type = packet->src_id_type;
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
+
+      /* Let the protocol handle the packet */
+      sock->protocol->execute(client->timeout_queue, 0,
+                             sock->protocol, sock->sock, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
                      "protocol active, packet dropped."));
     }
     break;
   case SILC_PACKET_KEY_EXCHANGE_2:
-    if (sock->protocol) {
+    if (sock->protocol && sock->protocol->protocol->type 
+       == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
       SilcClientKEInternalContext *proto_ctx = 
        (SilcClientKEInternalContext *)sock->protocol->context;
 
@@ -929,7 +1001,8 @@ void silc_client_packet_send(SilcClient client,
   /* Set the packet context pointers */
   packetdata.flags = 0;
   packetdata.type = type;
-  if (((SilcClientConnection)sock->user_data)->local_id_data)
+  if (sock->user_data && 
+      ((SilcClientConnection)sock->user_data)->local_id_data)
     packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
   else 
     packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
index e0ff8e767e118f43c0b0773cdccde019e512d61f..150236a76486e8e4ce63bb988033f7d5193fb377 100644 (file)
@@ -152,6 +152,13 @@ struct SilcClientStruct {
   SilcClientConnection *conns;
   unsigned int conns_count;
 
+  /* Table of listenning sockets in client.  Client can have listeners
+     (like key agreement protocol server) and those sockets are saved here.
+     This table is checked always if the connection object cannot be found
+     from the `conns' table. */
+  SilcSocketConnection *sockets;
+  unsigned int sockets_count;
+
   /* Generic cipher and hash objects. These can be used and referenced
      by the application as well. */
   SilcCipher none_cipher;
@@ -197,12 +204,18 @@ do {                                                      \
   int __i;                                             \
                                                        \
   for (__i = 0; __i < (__x)->conns_count; __i++)       \
-    if ((__x)->conns[__i]->sock->sock == (__fd))       \
+    if ((__x)->conns[__i] &&                           \
+       (__x)->conns[__i]->sock->sock == (__fd))        \
       break;                                           \
                                                        \
-  if (__i >= (__x)->conns_count)                       \
+  if (__i >= (__x)->conns_count) {                     \
     (__sock) = NULL;                                   \
- (__sock) = (__x)->conns[__i]->sock;                   \
+    for (__i = 0; __i < (__x)->sockets_count; __i++)   \
+      if ((__x)->sockets[__i] &&                       \
+         (__x)->sockets[__i]->sock == (__fd))          \
+        (__sock) = (__x)->sockets[__i];                        \
+  } else                                               \
+    (__sock) = (__x)->conns[__i]->sock;                        \
 } while(0)
 
 /* Prototypes (some of the prototypes are defined in the silcapi.h) */
index c80b1c620f2cbb8ac6746aa11e5c468ed08d6bef..cd0a240d66e8a088d643a58cea6f5b9a02462578 100644 (file)
@@ -69,6 +69,21 @@ static void silc_client_key_agreement_send_packet(SilcSKE ske,
   ske->sock->user_data = tmp;
 }
 
+/* Timeout callback that is called to close the connection and free the
+   socket connection data. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_close)
+{
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+
+  silc_schedule_unset_listen_fd(ke->sock->sock);
+  silc_schedule_unset_listen_fd(ke->fd);
+  silc_net_close_connection(ke->sock->sock);
+  silc_net_close_connection(ke->fd);
+  silc_socket_free(ke->sock);
+  silc_free(ke);
+}
+
 /* This callback is called after the key agreement protocol has been
    performed. This calls the final completion callback for the application. */
 
@@ -86,7 +101,8 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_final)
       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);
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
     silc_ske_free_key_material(ctx->keymat);
     goto out;
   }
@@ -94,8 +110,8 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_final)
   /* 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);
+  ke->completion(ke->client, ke->conn, ke->client_entry, 
+                SILC_KEY_AGREEMENT_OK, ctx->keymat, ke->context);
 
  out:
   silc_protocol_free(protocol);
@@ -106,10 +122,17 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_final)
   silc_task_unregister_by_callback(client->timeout_queue,
                                   silc_client_failure_callback);
   silc_task_unregister_by_fd(client->io_queue, ke->fd);
+  silc_schedule_unset_listen_fd(ke->fd);
+  silc_net_close_connection(ke->fd);
   if (ke->timeout)
     silc_task_unregister(client->timeout_queue, ke->timeout);
-  silc_socket_free(ke->sock);
-  silc_free(ke);
+  silc_client_del_socket(ke->client, ke->sock);
+
+  silc_task_register(client->timeout_queue, 0, 
+                    silc_client_key_agreement_close,
+                    (void *)ke, 0, 1, 
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
   silc_free(ctx);
 }
 
@@ -134,8 +157,11 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
                     "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);
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
     silc_task_unregister_by_fd(client->io_queue, ke->fd);
+    silc_schedule_unset_listen_fd(ke->fd);
+    silc_net_close_connection(ke->fd);
     if (ke->timeout)
       silc_task_unregister(client->timeout_queue, ke->timeout);
     silc_free(ke);
@@ -158,8 +184,11 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
     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);
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
     silc_task_unregister_by_fd(client->io_queue, ke->fd);
+    silc_schedule_unset_listen_fd(ke->fd);
+    silc_net_close_connection(ke->fd);
     if (ke->timeout)
       silc_task_unregister(client->timeout_queue, ke->timeout);
     silc_free(ke);
@@ -168,6 +197,7 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
   if (!newsocket->hostname)
     newsocket->hostname = strdup(newsocket->ip);
   newsocket->port = silc_net_get_remote_port(sock);
+  silc_client_add_socket(client, newsocket);
 
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
@@ -194,6 +224,7 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
      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);
 }
 
@@ -206,15 +237,21 @@ 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);
+  ke->completion(ke->client, ke->conn, ke->client_entry, 
+                SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
 
-  if (ke->sock)
+  if (ke->sock) {
+    silc_client_del_socket(ke->client, 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);
+  if (ke->fd)
+    silc_task_unregister_by_fd(ke->client->io_queue, ke->fd);
+  silc_schedule_unset_listen_fd(ke->fd);
+  silc_net_close_connection(ke->fd);
+  silc_free(ke);
 }
 
 /* Sends key agreement request to the remote client indicated by the
@@ -261,7 +298,7 @@ void silc_client_send_key_agreement(SilcClient client,
                                    void *context)
 {
   SilcSocketConnection sock = conn->sock;
-  SilcClientKeyAgreement ke;
+  SilcClientKeyAgreement ke = NULL;
   SilcBuffer buffer;
 
   assert(client_entry);
@@ -278,7 +315,8 @@ void silc_client_send_key_agreement(SilcClient client,
       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);
+      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+                NULL, context);
       silc_free(ke);
       return;
     }
@@ -300,15 +338,16 @@ void silc_client_send_key_agreement(SilcClient client,
     /* 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);
+    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);
+  buffer = silc_key_agreement_payload_encode(hostname, 
+                                            !ke ? port : 
+                                            silc_net_get_local_port(ke->fd));
 
   /* Send the key agreement packet to the client */
   silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT,
@@ -406,11 +445,12 @@ SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
       silc_schedule_unset_listen_fd(fd);
       silc_net_close_connection(fd);
       silc_task_unregister(client->io_queue, ctx->task);
+      silc_free(ctx->host);
       silc_free(ctx);
 
       /* Call the completion callback */
       ke->completion(ke->client, ke->conn, ke->client_entry, 
-                    NULL, ke->context);
+                    SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
       silc_free(ke);
     }
     return;
@@ -418,15 +458,16 @@ SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
 
   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->client_entry, ke->fd, ctx->host,
                                       ke->completion, ke->context);
   silc_free(ke);
+  silc_free(ctx->host);
+  silc_free(ctx);
 }
 
 /* Performs the actual key agreement protocol. Application may use this
@@ -459,6 +500,8 @@ void silc_client_perform_key_agreement(SilcClient client,
 {
   SilcClientKeyAgreement ke;
 
+  SILC_LOG_DEBUG(("Start"));
+
   assert(client_entry && hostname && port);
 
   ke = silc_calloc(1, sizeof(*ke));
@@ -471,7 +514,8 @@ void silc_client_perform_key_agreement(SilcClient client,
   /* 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);
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+              NULL, context);
     silc_free(ke);
     return;
   }
@@ -486,6 +530,7 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientEntry client_entry,
                                          int sock,
+                                         char *hostname,
                                          SilcKeyAgreementCallback completion,
                                          void *context)
 {
@@ -493,6 +538,8 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
   SilcClientKEInternalContext *proto_ctx;
   SilcProtocol protocol;
 
+  SILC_LOG_DEBUG(("Start"));
+
   assert(client_entry);
 
   ke = silc_calloc(1, sizeof(*ke));
@@ -505,6 +552,9 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
 
   /* Allocate new socket connection object */
   silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock);
+  silc_client_add_socket(client, ke->sock);
+  ke->sock->hostname = strdup(hostname);
+  ke->sock->port = silc_net_get_remote_port(sock);
 
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
@@ -550,8 +600,10 @@ void silc_client_abort_key_agreement(SilcClient client,
   assert(client_entry);
 
   if (client_entry->ke) {
-    if (client_entry->ke->sock)
+    if (client_entry->ke->sock) {
+      silc_client_del_socket(client_entry->ke->client, 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)
index c9d198c4b046577cf8bf0d421f74a7187ab2fba2..0234f0232c3fbb11ca02c3837ff9f87f3052bb89 100644 (file)
@@ -323,7 +323,7 @@ int silc_client_add_private_message_key(SilcClient client,
     return FALSE;
 
   /* Generate key if not provided */
-  if (!key && generate_key == TRUE) {
+  if (generate_key == TRUE) {
     len = 32;
     for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
     key = private_key;
index 29752426126ab5a8f3ef65d5968108bda46b8e2f..875b808c63c5ec8aa68264006cac784d59a09924 100644 (file)
@@ -499,7 +499,7 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
       
   /* Add pending callback */
   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
-                             ++conn->cmd_ident, 
+                             conn->cmd_ident, 
                              silc_client_get_client_by_id_destructor,
                              silc_client_command_get_client_by_id_callback, 
                              (void *)i);
index 5040a3014da88186d47b24c2db588a95b49e0b7d..f719bb5742c09262254abb856a11652603d28c76 100644 (file)
@@ -68,8 +68,9 @@ SilcSKEStatus silc_client_protocol_ke_verify_key(SilcSKE ske,
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Verify server key from user. */
-  if (!client->ops->verify_server_key(client, ctx->sock->user_data, 
+  /* Verify public key from user. */
+  if (!client->ops->verify_public_key(client, ctx->sock->user_data, 
+                                     ctx->sock->type,
                                      pk_data, pk_len, pk_type))
     return SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
 
index bdef1c76076a424672fccc8b27b6a84597b4a730..b9253518cd38cbd7886a6f40017b8fa612b43a8f 100644 (file)
 
 /* General definitions */
 
+/* Key agreement status types indicating the status of the protocol. */
+typedef enum {
+  SILC_KEY_AGREEMENT_OK,              /* Everything is Ok */
+  SILC_KEY_AGREEMENT_ERROR,           /* Unknown error occured */
+  SILC_KEY_AGREEMENT_FAILURE,         /* The protocol failed */
+  SILC_KEY_AGREEMENT_TIMEOUT,         /* The protocol timeout */
+} SilcKeyAgreementStatus;
+
 /* 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
@@ -47,6 +55,7 @@
 typedef void (*SilcKeyAgreementCallback)(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcClientEntry client_entry,
+                                        SilcKeyAgreementStatus status,
                                         SilcSKEKeyMaterial *key,
                                         void *context);
 
@@ -154,13 +163,14 @@ typedef struct {
                         unsigned char **auth_data,
                         unsigned int *auth_data_len);
 
-  /* Verifies received public key. The public key has been received from
-     a server. If user decides to trust the key may be saved as trusted
-     server key for later use. If user does not trust the key this returns
-     FALSE. If everything is Ok this returns TRUE. */ 
-  int (*verify_server_key)(SilcClient client, SilcClientConnection conn,
-                          unsigned char *pk, unsigned int pk_len,
-                          SilcSKEPKType pk_type);
+  /* Verifies received public key. The `conn_type' indicates which entity
+     (server, client etc.) has sent the public key. If user decides to trust
+     the key may be saved as trusted public key for later use. If user does
+     not trust the key this returns FALSE. If everything is Ok this returns
+     TRUE. */ 
+  int (*verify_public_key)(SilcClient client, SilcClientConnection conn,
+                          SilcSocketType conn_type, unsigned char *pk, 
+                          unsigned int pk_len, SilcSKEPKType pk_type);
 
   /* Ask (interact, that is) a passphrase from user. Returns the passphrase
      or NULL on error. */
@@ -255,6 +265,14 @@ SilcClientConnection silc_client_add_connection(SilcClient client,
 /* Removes connection from client. Frees all memory. */
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
 
+/* Adds listener socket to the listener sockets table. This function is
+   used to add socket objects that are listeners to the client.  This should
+   not be used to add other connection objects. */
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock);
+
+/* Deletes listener socket from the listener sockets table. */
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock);
+
 /* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
    key material between client and server.  This function can be called
    directly if application is performing its own connecting and does not
@@ -648,6 +666,7 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientEntry client_entry,
                                          int sock,
+                                         char *hostname,
                                          SilcKeyAgreementCallback completion,
                                          void *context);
 
index 3112f15830a6da0c35cd1ea141de8c8145140484..b879c42927573e86cf71870e67b9f33897e6dcb3 100644 (file)
@@ -316,7 +316,7 @@ int silc_packet_read(int sock, SilcBuffer dest)
       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
       return -2;
     }
-    SILC_LOG_ERROR(("Cannot read from socket: %d", strerror(errno)));
+    SILC_LOG_ERROR(("Cannot read from socket: %d:%s", sock, strerror(errno)));
     return -1;
   }
 
index 6914ccceecbb817e3dfa40d81efee11865f0123e..5b060d1e5f5989f06504248dfc02b187e5d7ac9e 100644 (file)
@@ -306,6 +306,21 @@ unsigned short silc_net_get_remote_port(int sock)
   return ntohs(remote.sin_port);
 }
 
+/* Return local port by socket. */
+
+unsigned short silc_net_get_local_port(int sock)
+{
+  struct sockaddr_in local;
+  int len;
+
+  memset(&local, 0, sizeof(local));
+  len = sizeof(local);
+  if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
+    return 0;
+
+  return ntohs(local.sin_port);
+}
+
 /* Return name of localhost. */
 
 char *silc_net_localhost()
index 7cbda8c4a46ae57aa0e94c15e23bf88addd1111c..b4301253939b0e618b44e9481da69fbad9829ab5 100644 (file)
@@ -33,6 +33,7 @@ int silc_net_set_socket_opt(int sock, int level, int option, int on);
 int silc_net_is_ip(const char *addr);
 void silc_net_check_host_by_sock(int sock, char **hostname, char **ip);
 unsigned short silc_net_get_remote_port(int sock);
+unsigned short silc_net_get_local_port(int sock);
 char *silc_net_localhost();
 
 #endif