updates.
authorPekka Riikonen <priikone@silcnet.org>
Sat, 26 May 2001 13:55:13 +0000 (13:55 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 26 May 2001 13:55:13 +0000 (13:55 +0000)
15 files changed:
CHANGES
TODO
apps/irssi/src/silc/core/silc-channels.c
apps/irssi/src/silc/core/silc-core.c
apps/irssi/src/silc/core/silc-servers.c
apps/silc/client_ops.c
apps/silc/client_ops.h
apps/silcd/protocol.c
lib/silcclient/command.c
lib/silcclient/protocol.c
lib/silcclient/protocol.h
lib/silcclient/silcapi.h
lib/silcske/silcske.c
lib/silcske/silcske.h
lib/silcske/silcske_status.h

diff --git a/CHANGES b/CHANGES
index be2ab6e5d246e880480ae5083f433d5a0be41050..df4adf938a8fe91e782206712a8e584dc35662fd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,50 @@
+Sat May 26 12:13:37 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed the ask_passphrase client operation to be ascynchronous.
+         It has now a completion callback and a context that the 
+         application must call after it has got the passphrase from
+         the user.  Affected files lib/silcclient/silcapi.h,
+         lib/silcclient/protocol.c, lib/silcclient/command.c and
+         silc/client_ops.c.
+
+         Added SilcAskPassphrase callback that the application calls
+         to deliver the passphrase to the library.
+
+       * Changed the SKE protocol's SilcSKEVerifyCb to be asynchronous.
+         The public key verification and especially a certificate
+         verification is asynchronous procedure.
+
+         Added new SILC_SKE_STATUS_PENDING status to indicate the
+         request is pending and a callback will be called to finalize
+         the request.
+
+         Added also SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED status to
+         indicate that remote end did not send its public key (or
+         certificate), even though we require it.  Added check for this
+         condition in the SKE.  This was a security bug, now fixed.
+
+         Defined new SilcSKEVerifyCbCompletion callback that is called
+         when the verification process is completed.
+
+         The affected files lib/silcske/silcske_status.h and
+         lib/silcske/silcske.[ch].
+
+       * Changed the verify_public_key client operation to be async
+         as well.  Defined SilcVerifyPublicKey callback that is used to
+         indicate the success of the public key verification process.
+
+         Changed the server and client to use the new async client 
+         operations.
+
+       * Changed the Irssi SILC client's internal scheduler to be called
+         twice as many times as it used to be.  As a result the client
+         should be a bit faster now.  Affected file is
+         irssi/src/silc/core/silc-core.c.
+
+       * Added support to Irssi SILC client of asynchronous public key
+         verification and passphrase inquiry.  Affected file is
+         irssi/src/silc/core/silc-core.c.
+
 Fri May 25 14:38:38 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Do not say "You have left channel %s" in client library.
diff --git a/TODO b/TODO
index b695701e9bb0fd7526bd65f9f6fc065841ea4616..04e9e8d04fe56ce291ad12b07f8de5deeab3ae0b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -19,9 +19,9 @@ TODO/bugs In SILC Client Library
 TODO/bugs In SILC Server
 ========================
 
- o Save channel names in the ID Cache always as lowered characters,
-   though allow mixed case characters in the channel entry but the ID
-   cache does not handle loose data searching anymore.
+ o The SKE protocol in the server does not verify the remote hosts 
+   (the router's) public key at all.  All public keys are accepted without
+   verification - this obviously is not secure.
 
  o When server quits and all clients of that server are removed from all
    channels the channel keys are re-generated for all clients.  This is
index c00ec25891b2541e604b4b4632c5134d2ed6bb55..940d87125f26cfcd4a405ad1c72aa46b557cc17d 100644 (file)
@@ -337,7 +337,7 @@ static void event_cumode(SILC_SERVER_REC *server, va_list va)
   
   printtext(server, channel->channel_name, MSGLEVEL_MODES,
            "cumode/%s/%s [%s] by %s", destclient->nickname, 
-           channel->channel_name, mode, client->nickname);
+           channel->channel_name, modestr, client->nickname);
   
   g_free(modestr);
 }
index fed647f51260c622bd6c854266d47c96754a5df7..0ef7af681b8ddf5fa21db7f7ce49d78c46dc5645 100644 (file)
@@ -37,6 +37,7 @@
 #include "settings.h"
 #include "fe-common/core/printtext.h"
 #include "fe-common/core/fe-channels.h"
+#include "fe-common/core/keyboard.h"
 
 /* Command line option variables */
 static bool opt_create_keypair = FALSE;
@@ -81,14 +82,18 @@ static void
 silc_command_reply(SilcClient client, SilcClientConnection conn,
                   SilcCommandPayload cmd_payload, int success,
                   SilcCommand command, SilcCommandStatus status, ...);
-
-static int silc_verify_public_key(SilcClient client,
-                                 SilcClientConnection conn, 
-                                 SilcSocketType conn_type,
-                                 unsigned char *pk, uint32 pk_len,
-                                 SilcSKEPKType pk_type);
-static unsigned char *silc_ask_passphrase(SilcClient client,
-                                         SilcClientConnection conn);
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context);
+static void 
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcSocketType conn_type, unsigned char *pk, 
+                      uint32 pk_len, SilcSKEPKType pk_type,
+                      SilcVerifyPublicKey completion, void *context);
+static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                               SilcAskPassphrase completion, void *context);
 static int 
 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
                     char *hostname, uint16 port,
@@ -788,8 +793,9 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
       
       if (id_type == SILC_ID_CLIENT) {
-       silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
-                              pk, pk_len, SILC_SKE_PK_TYPE_SILC);
+       silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
+                                       pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+                                       NULL, NULL);
       }
       
       silc_free(pk);
@@ -819,25 +825,234 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
   va_end(vp);
 }
 
-/* Verifies received public key. If user decides to trust the key it is
-   saved as public server key for later use. If user does not trust the
-   key this returns FALSE. */
+/* Internal routine to verify public key. If the `completion' is provided
+   it will be called to indicate whether public was verified or not. */
 
-static int silc_verify_public_key(SilcClient client,
-                                 SilcClientConnection conn, 
-                                 SilcSocketType conn_type,
-                                 unsigned char *pk, uint32 pk_len,
-                                 SilcSKEPKType pk_type)
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  char *filename;
+  char *entity;
+  unsigned char *pk;
+  uint32 pk_len;
+  SilcSKEPKType pk_type;
+  SilcVerifyPublicKey completion;
+  void *context;
+} *PublicKeyVerify;
+
+static void verify_public_key_completion(const char *line, void *context)
 {
-  return TRUE;
+  PublicKeyVerify verify = (PublicKeyVerify)context;
+
+  if (line[0] == 'Y' || line[0] == 'y') {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(TRUE, verify->context);
+
+    /* Save the key for future checking */
+    silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
+                                  verify->pk_len, SILC_PKCS_FILE_PEM);
+  } else {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(FALSE, verify->context);
+
+    silc_say(verify->client, 
+            verify->conn, "Will not accept the %s key", verify->entity);
+  }
+
+  silc_free(verify->filename);
+  silc_free(verify->entity);
+  silc_free(verify);
+}
+
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context)
+{
+  int i;
+  char file[256], filename[256], *fingerprint;
+  struct passwd *pw;
+  struct stat st;
+  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
+                 "server" : "client");
+  PublicKeyVerify verify;
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    silc_say(client, conn, "We don't support %s public key type %d", 
+            entity, pk_type);
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  pw = getpwuid(getuid());
+  if (!pw) {
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  memset(filename, 0, sizeof(filename));
+  memset(file, 0, sizeof(file));
+
+  if (conn_type == SILC_SOCKET_TYPE_SERVER ||
+      conn_type == SILC_SOCKET_TYPE_ROUTER) {
+    snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+            conn->sock->hostname, conn->sock->port);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+  } else {
+    /* Replace all whitespaces with `_'. */
+    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+    for (i = 0; i < strlen(fingerprint); i++)
+      if (fingerprint[i] == ' ')
+       fingerprint[i] = '_';
+    
+    snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+    silc_free(fingerprint);
+  }
+
+  /* Take fingerprint of the public key */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+
+  verify = silc_calloc(1, sizeof(*verify));
+  verify->client = client;
+  verify->conn = conn;
+  verify->filename = strdup(filename);
+  verify->entity = strdup(entity);
+  verify->pk = pk;
+  verify->pk_len = pk_len;
+  verify->pk_type = pk_type;
+  verify->completion = completion;
+  verify->context = context;
+
+  /* Check whether this key already exists */
+  if (stat(filename, &st) < 0) {
+    /* Key does not exist, ask user to verify the key and save it */
+
+    silc_say(client, conn, "Received %s public key", entity);
+    silc_say(client, conn, "Fingerprint for the %s key is", entity);
+    silc_say(client, conn, "%s", fingerprint);
+
+    keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                           "Would you like to accept the key (y/n)? ", 0,
+                           verify);
+    silc_free(fingerprint);
+    return;
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    uint32 encpk_len;
+
+    /* Load the key file */
+    if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                  SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                    SILC_PKCS_FILE_BIN)) {
+       silc_say(client, conn, "Received %s public key", entity);
+       silc_say(client, conn, "Fingerprint for the %s key is", entity);
+       silc_say(client, conn, "%s", fingerprint);
+       silc_say(client, conn, "Could not load your local copy of the %s key",
+                entity);
+       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                               "Would you like to accept the key "
+                               "anyway (y/n)? ", 0,
+                               verify);
+       silc_free(fingerprint);
+       return;
+      }
+  
+    /* Encode the key data */
+    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+    if (!encpk) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "Your local copy of the %s key is malformed",
+              entity);
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             "Would you like to accept the key "
+                             "anyway (y/n)? ", 0,
+                             verify);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Compare the keys */
+    if (memcmp(encpk, pk, encpk_len)) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "%s key does not match with your local copy",
+              entity);
+      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");
+
+      /* Ask user to verify the key and save it */
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             "Would you like to accept the key "
+                             "anyway (y/n)? ", 0,
+                             verify);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Local copy matched */
+    if (completion)
+      completion(TRUE, context);
+    silc_free(fingerprint);
+  }
+}
+
+/* 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. The 
+   `completion' must be called after the public key has been verified. */
+
+static void 
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcSocketType conn_type, unsigned char *pk, 
+                      uint32 pk_len, SilcSKEPKType pk_type,
+                      SilcVerifyPublicKey completion, void *context)
+{
+  silc_verify_public_key_internal(client, conn, conn_type, pk,
+                                 pk_len, pk_type,
+                                 completion, context);
 }
 
 /* Asks passphrase from user on the input line. */
 
-static unsigned char *silc_ask_passphrase(SilcClient client,
-                                         SilcClientConnection conn)
+typedef struct {
+  SilcAskPassphrase completion;
+  void *context;
+} *AskPassphrase;
+
+static void ask_passphrase_completion(const char *passphrase, void *context)
 {
-  return NULL;
+  AskPassphrase p = (AskPassphrase)context;
+  p->completion((unsigned char *)passphrase, 
+               passphrase ? strlen(passphrase) : 0, p->context);
+  silc_free(p);
+}
+
+static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                               SilcAskPassphrase completion, void *context)
+{
+  AskPassphrase p = silc_calloc(1, sizeof(*p));
+  p->completion = completion;
+  p->context = context;
+
+  keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
+                         "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
 }
 
 /* Find authentication method and authentication data by hostname and
index 062b6049fb221fc66095b5e939a0160a002d3018..8e9089361203a1c1ff850200471b4d8aa3fd8770 100644 (file)
@@ -327,6 +327,8 @@ void silc_server_init(void)
   command_bind("getkey", MODULE_NAME, (SIGNAL_FUNC) command_self);
 
   command_set_options("connect", "+silcnet");
+
+  settings_add_bool("server", "skip_motd", FALSE);
 }
 
 void silc_server_deinit(void)
index 62849ce157b41ebd90cb87ecd378759ac01506b9..33de05c6690b4b2c0c2820ec6c4777d1b6648a7d 100644 (file)
 
 #include "clientincludes.h"
 
+static bool 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type)
+{
+  int i;
+  char file[256], filename[256], *fingerprint;
+  struct passwd *pw;
+  struct stat st;
+  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
+                 "server" : "client");
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    silc_say(client, conn, "We don't support %s public key type %d", 
+            entity, pk_type);
+    return FALSE;
+  }
+
+  pw = getpwuid(getuid());
+  if (!pw)
+    return FALSE;
+
+  memset(filename, 0, sizeof(filename));
+  memset(file, 0, sizeof(file));
+
+  if (conn_type == SILC_SOCKET_TYPE_SERVER ||
+      conn_type == SILC_SOCKET_TYPE_ROUTER) {
+    snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+            conn->sock->hostname, conn->sock->port);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+  } else {
+    /* Replace all whitespaces with `_'. */
+    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+    for (i = 0; i < strlen(fingerprint); i++)
+      if (fingerprint[i] == ' ')
+       fingerprint[i] = '_';
+    
+    snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+    silc_free(fingerprint);
+  }
+
+  /* Take fingerprint of the public key */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+
+  /* Check whether this key already exists */
+  if (stat(filename, &st) < 0) {
+
+    silc_say(client, conn, "Received %s public key", entity);
+    silc_say(client, conn, "Fingerprint for the %s key is", entity);
+    silc_say(client, conn, "%s", fingerprint);
+
+    /* Ask user to verify the key and save it */
+    if (silc_client_ask_yes_no(client, 
+       "Would you like to accept the key (y/n)? "))
+      {
+       /* Save the key for future checking */
+       silc_pkcs_save_public_key_data(filename, pk, pk_len, 
+                                      SILC_PKCS_FILE_PEM);
+       silc_free(fingerprint);
+       return TRUE;
+      }
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    uint32 encpk_len;
+
+    /* Load the key file */
+    if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                  SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                    SILC_PKCS_FILE_BIN)) {
+       silc_say(client, conn, "Received %s public key", entity);
+       silc_say(client, conn, "Fingerprint for the %s key is", entity);
+       silc_say(client, conn, "%s", fingerprint);
+       silc_say(client, conn, "Could not load your local copy of the %s key",
+                entity);
+       if (silc_client_ask_yes_no(client, 
+          "Would you like to accept the key anyway (y/n)? "))
+         {
+           /* Save the key for future checking */
+           unlink(filename);
+           silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                          SILC_PKCS_FILE_PEM);
+           silc_free(fingerprint);
+           return TRUE;
+         }
+       
+       silc_free(fingerprint);
+       return FALSE;
+      }
+  
+    /* Encode the key data */
+    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+    if (!encpk) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "Your local copy of the %s key is malformed",
+              entity);
+      if (silc_client_ask_yes_no(client, 
+         "Would you like to accept the key anyway (y/n)? "))
+       {
+         /* Save the key for future checking */
+         unlink(filename);
+         silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                        SILC_PKCS_FILE_PEM);
+         silc_free(fingerprint);
+         return TRUE;
+       }
+
+      silc_free(fingerprint);
+      return FALSE;
+    }
+
+    if (memcmp(encpk, pk, encpk_len)) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "%s key does not match with your local copy",
+              entity);
+      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");
+      
+      /* Ask user to verify the key and save it */
+      if (silc_client_ask_yes_no(client, 
+         "Would you like to accept the key anyway (y/n)? "))
+       {
+         /* Save the key for future checking */
+         unlink(filename);
+         silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                        SILC_PKCS_FILE_PEM);
+         silc_free(fingerprint);
+         return TRUE;
+       }
+
+      silc_say(client, conn, "Will not accept the %s key", entity);
+      silc_free(fingerprint);
+      return FALSE;
+    }
+
+    /* Local copy matched */
+    silc_free(fingerprint);
+    return TRUE;
+  }
+
+  silc_say(client, conn, "Will not accept the %s key", entity);
+  silc_free(fingerprint);
+  return FALSE;
+}
+
 /* Prints a message with three star (*) sign before the actual message
    on the current output window. This is used to print command outputs
    and error messages. */
@@ -905,8 +1062,9 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
        pk = silc_pkcs_public_key_encode(public_key, &pk_len);
 
        if (id_type == SILC_ID_CLIENT) {
-         silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
-                                pk, pk_len, SILC_SKE_PK_TYPE_SILC);
+         silc_verify_public_key_internal(client, conn, 
+                                         SILC_SOCKET_TYPE_CLIENT,
+                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC);
        }
 
        silc_free(pk);
@@ -963,12 +1121,11 @@ void silc_disconnect(SilcClient client, SilcClientConnection conn)
 
 /* Asks passphrase from user on the input line. */
 
-unsigned char *silc_ask_passphrase(SilcClient client, 
-                                  SilcClientConnection conn)
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context)
 {
   SilcClientInternal app = (SilcClientInternal)conn->client->application;
   char pass1[256], pass2[256];
-  char *ret;
   int try = 3;
 
   while(try) {
@@ -997,180 +1154,33 @@ unsigned char *silc_ask_passphrase(SilcClient client,
     try--;
   }
 
-  ret = silc_calloc(strlen(pass1), sizeof(char));
-  memcpy(ret, pass1, strlen(pass1));
-
-  memset(pass1, 0, sizeof(pass1));
-  memset(pass2, 0, sizeof(pass2));
-
   wattroff(app->screen->input_win, A_INVIS);
   silc_screen_input_reset(app->screen);
 
-  return ret;
+  /* Deliver the passphrase to the library */
+  completion(pass1, strlen(pass1), context);
+
+  memset(pass1, 0, sizeof(pass1));
+  memset(pass2, 0, sizeof(pass2));
 }
 
-/* Verifies received public key. If user decides to trust the key it is
-   saved as public server key for later use. If user does not trust the
-   key this returns FALSE. */
+/* 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. The 
+   `completion' must be called after the public key has been verified. */
 
-int silc_verify_public_key(SilcClient client,
-                          SilcClientConnection conn, 
-                          SilcSocketType conn_type,
-                          unsigned char *pk, uint32 pk_len,
-                          SilcSKEPKType pk_type)
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context)
 {
-  int i;
-  char filename[256];
-  char file[256];
-  char *fingerprint;
-  struct passwd *pw;
-  struct stat st;
-  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
-                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
-                 "server" : "client");
-
-  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
-    silc_say(client, conn, "We don't support %s public key type %d", 
-            entity, pk_type);
-    return FALSE;
-  }
-
-  pw = getpwuid(getuid());
-  if (!pw)
-    return FALSE;
-
-  memset(filename, 0, sizeof(filename));
-  memset(file, 0, sizeof(file));
-
-  if (conn_type == SILC_SOCKET_TYPE_SERVER ||
-      conn_type == SILC_SOCKET_TYPE_ROUTER) {
-    snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
-            conn->sock->hostname, conn->sock->port);
-    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
-            pw->pw_dir, entity, file);
-  } else {
-    /* Replace all whitespaces with `_'. */
-    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-    for (i = 0; i < strlen(fingerprint); i++)
-      if (fingerprint[i] == ' ')
-       fingerprint[i] = '_';
-    
-    snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
-    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
-            pw->pw_dir, entity, file);
-    silc_free(fingerprint);
-  }
-
-  /* Take fingerprint of the public key */
-  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-
-  /* Check whether this key already exists */
-  if (stat(filename, &st) < 0) {
-
-    silc_say(client, conn, "Received %s public key", entity);
-    silc_say(client, conn, "Fingerprint for the %s key is", entity);
-    silc_say(client, conn, "%s", fingerprint);
-
-    /* Ask user to verify the key and save it */
-    if (silc_client_ask_yes_no(client, 
-       "Would you like to accept the key (y/n)? "))
-      {
-       /* Save the key for future checking */
-       silc_pkcs_save_public_key_data(filename, pk, pk_len, 
-                                      SILC_PKCS_FILE_PEM);
-       silc_free(fingerprint);
-       return TRUE;
-      }
-  } else {
-    /* The key already exists, verify it. */
-    SilcPublicKey public_key;
-    unsigned char *encpk;
-    uint32 encpk_len;
-
-    /* Load the key file */
-    if (!silc_pkcs_load_public_key(filename, &public_key, 
-                                  SILC_PKCS_FILE_PEM))
-      if (!silc_pkcs_load_public_key(filename, &public_key, 
-                                    SILC_PKCS_FILE_BIN)) {
-       silc_say(client, conn, "Received %s public key", entity);
-       silc_say(client, conn, "Fingerprint for the %s key is", entity);
-       silc_say(client, conn, "%s", fingerprint);
-       silc_say(client, conn, "Could not load your local copy of the %s key",
-                entity);
-       if (silc_client_ask_yes_no(client, 
-          "Would you like to accept the key anyway (y/n)? "))
-         {
-           /* Save the key for future checking */
-           unlink(filename);
-           silc_pkcs_save_public_key_data(filename, pk, pk_len,
-                                          SILC_PKCS_FILE_PEM);
-           silc_free(fingerprint);
-           return TRUE;
-         }
-       
-       silc_free(fingerprint);
-       return FALSE;
-      }
-  
-    /* Encode the key data */
-    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
-    if (!encpk) {
-      silc_say(client, conn, "Received %s public key", entity);
-      silc_say(client, conn, "Fingerprint for the %s key is", entity);
-      silc_say(client, conn, "%s", fingerprint);
-      silc_say(client, conn, "Your local copy of the %s key is malformed",
-              entity);
-      if (silc_client_ask_yes_no(client, 
-         "Would you like to accept the key anyway (y/n)? "))
-       {
-         /* Save the key for future checking */
-         unlink(filename);
-         silc_pkcs_save_public_key_data(filename, pk, pk_len,
-                                        SILC_PKCS_FILE_PEM);
-         silc_free(fingerprint);
-         return TRUE;
-       }
-
-      silc_free(fingerprint);
-      return FALSE;
-    }
-
-    if (memcmp(encpk, pk, encpk_len)) {
-      silc_say(client, conn, "Received %s public key", entity);
-      silc_say(client, conn, "Fingerprint for the %s key is", entity);
-      silc_say(client, conn, "%s", fingerprint);
-      silc_say(client, conn, "%s key does not match with your local copy",
-              entity);
-      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");
-      
-      /* Ask user to verify the key and save it */
-      if (silc_client_ask_yes_no(client, 
-         "Would you like to accept the key anyway (y/n)? "))
-       {
-         /* Save the key for future checking */
-         unlink(filename);
-         silc_pkcs_save_public_key_data(filename, pk, pk_len,
-                                        SILC_PKCS_FILE_PEM);
-         silc_free(fingerprint);
-         return TRUE;
-       }
-
-      silc_say(client, conn, "Will not accept the %s key", entity);
-      silc_free(fingerprint);
-      return FALSE;
-    }
-
-    /* Local copy matched */
-    silc_free(fingerprint);
-    return TRUE;
+  if (silc_verify_public_key_internal(client, conn, conn_type, pk,
+                                     pk_len, pk_type)) {
+    completion(TRUE, context);
+    return;
   }
 
-  silc_say(client, conn, "Will not accept the %s key", entity);
-  silc_free(fingerprint);
-  return FALSE;
+  completion(FALSE, context);
 }
 
 /* Find authentication method and authentication data by hostname and
index ebcd0e3178902e8600e5c1c39deed68de7320cbf..e803af4d720285389fbe854191224fcc0d892dd9 100644 (file)
@@ -39,13 +39,12 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
                        SilcCommand command, SilcCommandStatus status, ...);
 void silc_connect(SilcClient client, SilcClientConnection conn, int success);
 void silc_disconnect(SilcClient client, SilcClientConnection conn);
-unsigned char *silc_ask_passphrase(SilcClient client, 
-                                  SilcClientConnection conn);
-int silc_verify_public_key(SilcClient client,
-                          SilcClientConnection conn, 
-                          SilcSocketType conn_type,
-                          unsigned char *pk, uint32 pk_len,
-                          SilcSKEPKType pk_type);
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context);
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context);
 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
                         char *hostname, uint16 port,
                         SilcProtocolAuthMeth *auth_meth,
index 6069caaf98db9b125c8cd712a9548cb557e892ab..dbda9d846c674f7149f6ec4ac22a0da60a17d4a0 100644 (file)
@@ -197,6 +197,42 @@ SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
   return status;
 }
 
+/* Callback that is called by the SKE to indicate that it is safe to
+   continue the execution of the protocol. This is used only if we are
+   initiator.  Is given as argument to the silc_ske_initiator_finish
+   function. This is called due to the fact that the public key verification
+   process is asynchronous and we must not continue the protocl until
+   the public key has been verified and this callback is called. */
+
+static void silc_server_protocol_ke_finish(SilcSKE ske, void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerKEInternalContext *ctx = 
+    (SilcServerKEInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->status != SILC_SKE_STATUS_OK) {
+    SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                     ske->status));
+    SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
+                   ske->status));
+    
+    protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    protocol->execute(server->timeout_queue, 0, protocol, 0, 0, 300000);
+    return;
+  }
+
+  /* Send Ok to the other end. We will end the protocol as responder
+     sends Ok to us when we will take the new keys into use. */
+  if (ctx->responder == FALSE)
+    silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
+
+  /* End the protocol on the next round */
+  protocol->state = SILC_PROTOCOL_STATE_END;
+}
+
 /* Performs key exchange protocol. This is used for both initiator
    and responder key exchange. This is performed always when accepting
    new connection to the server. This may be called recursively. */
@@ -207,7 +243,7 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSKEStatus status = 0;
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -252,6 +288,10 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
                                          context);
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
        SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
                          status));
@@ -290,6 +330,10 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
                                            NULL, NULL);
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
        SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
                          status));
@@ -316,6 +360,12 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
        /* Process the received Key Exchange 1 Payload packet from
           the initiator. This also creates our parts of the Diffie
           Hellman algorithm. */
+       /* XXX TODO: If mutual authentication flag is set then the
+          verify_key callback should be set to verify the remote ends
+          public key!! */
+       /* XXX TODO: when the verify_key is set then the `callback'
+          must be set as well as the verify_key is asynchronous
+          (take a look to silc_ske_initiator_finish for example. */
        status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer, 
                                            NULL, NULL, NULL, NULL);
       } else {
@@ -330,6 +380,10 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
                                     context);
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
        SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
                          status));
@@ -361,13 +415,23 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
                                    SILC_SKE_PK_TYPE_SILC,
                                    silc_server_protocol_ke_send_packet,
                                    context);
+
+       /* End the protocol on the next round */
+       protocol->state = SILC_PROTOCOL_STATE_END;
       } else {
        /* Finish the protocol. This verifies the Key Exchange 2 payload
           sent by responder. */
+       /* XXX TODO: the verify_key callback is not set!!! */
        status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer, 
-                                          NULL, NULL, NULL, NULL);
+                                          NULL, NULL, 
+                                          silc_server_protocol_ke_finish, 
+                                          context);
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
        SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
                          status));
@@ -378,14 +442,6 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
        protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
        return;
       }
-
-      /* Send Ok to the other end. We will end the protocol as responder
-        sends Ok to us when we will take the new keys into use. */
-      if (ctx->responder == FALSE)
-       silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
-
-      /* End the protocol on the next round */
-      protocol->state = SILC_PROTOCOL_STATE_END;
     }
     break;
 
index b3d8e4f68c0342d469aefdf601a67e46ab30b1f8..ac2324cbd87ef896598059f79cd164f3e32a101b 100644 (file)
@@ -1578,15 +1578,44 @@ SILC_CLIENT_CMD_FUNC(kick)
   silc_client_command_free(cmd);
 }
 
+static void silc_client_command_oper_send(unsigned char *data,
+                                         uint32 data_len, void *context)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, auth;
+
+  if (cmd->argc == 3) {
+    /* Pulic key auth XXX TODO */
+    auth = NULL;
+  } else {
+    /* Encode the authentication payload */
+    auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                   data, data_len);
+  }
+
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
+                                         1, cmd->argv[1], 
+                                         strlen(cmd->argv[1]),
+                                         2, auth->data, auth->len);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+
+  silc_buffer_free(buffer);
+  silc_buffer_free(auth);
+
+  /* Notify application */
+  COMMAND;
+}
+
 /* OPER command. Used to obtain server operator privileges. */
 
 SILC_CLIENT_CMD_FUNC(oper)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
   unsigned char *auth_data;
-  SilcBuffer auth;
+  uint32 auth_data_len = 0;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1608,19 +1637,41 @@ SILC_CLIENT_CMD_FUNC(oper)
     goto out;
   } else {
     /* Get passphrase */
+    cmd->client->ops->ask_passphrase(cmd->client, conn,
+                                    silc_client_command_oper_send,
+                                    context);
+    return;
+  }
 
-    auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
-    if (!auth_data) {
-      COMMAND_ERROR;
-      goto out;
-    }
+  silc_client_command_oper_send(auth_data, auth_data_len, context);
+
+  memset(auth_data, 0, auth_data_len);
+  silc_free(auth_data);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+static void silc_client_command_silcoper_send(unsigned char *data,
+                                             uint32 data_len, void *context)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, auth;
 
+  if (cmd->argc == 3) {
+    /* Pulic key auth XXX TODO */
+    auth = NULL;
+  } else {
     /* Encode the authentication payload */
     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
-                                   auth_data, strlen(auth_data));
+                                   data, data_len);
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
                                          1, cmd->argv[1], 
                                          strlen(cmd->argv[1]),
                                          2, auth->data, auth->len);
@@ -1629,14 +1680,9 @@ SILC_CLIENT_CMD_FUNC(oper)
 
   silc_buffer_free(buffer);
   silc_buffer_free(auth);
-  memset(auth_data, 0, strlen(auth_data));
-  silc_free(auth_data);
 
   /* Notify application */
   COMMAND;
-
- out:
-  silc_client_command_free(cmd);
 }
 
 /* SILCOPER command. Used to obtain router operator privileges. */
@@ -1645,9 +1691,8 @@ SILC_CLIENT_CMD_FUNC(silcoper)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
   unsigned char *auth_data;
-  SilcBuffer auth;
+  uint32 auth_data_len = 0;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1669,28 +1714,15 @@ SILC_CLIENT_CMD_FUNC(silcoper)
     goto out;
   } else {
     /* Get passphrase */
-
-    auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
-    if (!auth_data) {
-      COMMAND_ERROR;
-      goto out;
-    }
-
-    /* Encode the authentication payload */
-    auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
-                                   auth_data, strlen(auth_data));
+    cmd->client->ops->ask_passphrase(cmd->client, conn,
+                                    silc_client_command_silcoper_send,
+                                    context);
+    return;
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
-                                         1, cmd->argv[1], 
-                                         strlen(cmd->argv[1]),
-                                         2, auth->data, auth->len);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_client_command_silcoper_send(auth_data, auth_data_len, context);
 
-  silc_buffer_free(buffer);
-  silc_buffer_free(auth);
-  memset(auth_data, 0, strlen(auth_data));
+  memset(auth_data, 0, auth_data_len);
   silc_free(auth_data);
 
   /* Notify application */
index d4cfae976bb5c8a0f40b8d8ed8023d1824dd3463..f82bbbeb71805efa3d16ccf6b75789674f904de5 100644 (file)
@@ -52,29 +52,57 @@ void silc_client_protocol_ke_send_packet(SilcSKE ske,
                          packet->data, packet->len, TRUE);
 }
 
+/* Public key verification callback. Called by the application. */
+
+typedef struct {
+  SilcSKE ske;
+  SilcSKEVerifyCbCompletion completion;
+  void *completion_context;
+} *VerifyKeyContext;
+
+static void silc_client_verify_key_cb(bool success, void *context)
+{
+  VerifyKeyContext verify = (VerifyKeyContext)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Call the completion callback back to the SKE */
+  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK : 
+                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, 
+                    verify->completion_context);
+
+  silc_free(verify);
+}
+
 /* Callback that is called when we have received KE2 payload from
    responder. We try to verify the public key now. */
 
-SilcSKEStatus silc_client_protocol_ke_verify_key(SilcSKE ske,
-                                                unsigned char *pk_data,
-                                                uint32 pk_len,
-                                                SilcSKEPKType pk_type,
-                                                void *context)
+void silc_client_protocol_ke_verify_key(SilcSKE ske,
+                                       unsigned char *pk_data,
+                                       uint32 pk_len,
+                                       SilcSKEPKType pk_type,
+                                       void *context,
+                                       SilcSKEVerifyCbCompletion completion,
+                                       void *completion_context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
   SilcClientKEInternalContext *ctx = 
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
+  VerifyKeyContext verify;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* 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;
+  verify = silc_calloc(1, sizeof(*verify));
+  verify->ske = ske;
+  verify->completion = completion;
+  verify->completion_context = completion_context;
 
-  return SILC_SKE_STATUS_OK;
+  /* Verify public key from user. */
+  client->ops->verify_public_key(client, ctx->sock->user_data, 
+                                ctx->sock->type,
+                                pk_data, pk_len, pk_type,
+                                silc_client_verify_key_cb, verify);
 }
 
 /* Sets the negotiated key material into use for particular connection. */
@@ -193,6 +221,64 @@ SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
   return status;
 }
 
+/* Callback that is called by the SKE to indicate that it is safe to
+   continue the execution of the protocol. Is given as argument to the 
+   silc_ske_initiator_finish or silc_ske_responder_phase_2 functions. 
+   This is called due to the fact that the public key verification
+   process is asynchronous and we must not continue the protocl until
+   the public key has been verified and this callback is called. */
+
+static void silc_client_protocol_ke_continue(SilcSKE ske,
+                                            void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientConnection conn = ctx->sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->status != SILC_SKE_STATUS_OK) {
+    if (ske->status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) {
+      client->ops->say(client, conn, 
+                      "Received unsupported server %s public key",
+                      ctx->sock->hostname);
+    } else if (ske->status == SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED) {
+      client->ops->say(client, conn, 
+                      "Remote host did not send its public key, even though "
+                      "it must send it");
+    } else {
+      client->ops->say(client, conn,
+                      "Error during key exchange protocol with server %s",
+                      ctx->sock->hostname);
+    }
+    
+    protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    protocol->execute(client->timeout_queue, 0, protocol, 0, 0, 0);
+    return;
+  }
+
+  /* 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. Do this
+     if we are initiator. This is happens when this callback was sent
+     to silc_ske_initiator_finish function. */
+  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;
+  }
+
+  /* Advance protocol state and call the next state if we are responder. 
+     This happens when this callback was sent to silc_ske_responder_phase_2
+     function. */
+  if (ctx->responder == TRUE) {
+    protocol->state++;
+    protocol->execute(client->timeout_queue, 0, protocol, 0, 0, 100000);
+  }
+}
+
 /* Performs key exchange protocol. This is used for both initiator
    and responder key exchange. This may be called recursively. */
 
@@ -203,7 +289,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcClientConnection conn = ctx->sock->user_data;
-  SilcSKEStatus status = 0;
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -247,6 +333,10 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
                                          context);
       }
 
+      /* Return now if the procedure is pending */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
        SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
                          status));
@@ -310,9 +400,12 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       if (ctx->responder == TRUE) {
        /* Process the received Key Exchange 1 Payload packet from
           the initiator. This also creates our parts of the Diffie
-          Hellman algorithm. */
+          Hellman algorithm. The silc_client_protocol_ke_continue will
+          be called after the public key has been verified. */
        status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer, 
-                                           ctx->verify, context, NULL, NULL);
+                                           ctx->verify, context, 
+                                           silc_client_protocol_ke_continue,
+                                           context);
       } else {
        /* Call the Phase-2 function. This creates Diffie Hellman
           key exchange parameters and sends our public part inside
@@ -322,8 +415,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
                                            client->private_key,
                                            ctx->send_packet,
                                            context);
+       protocol->state++;
       }
 
+      /* Return now if the procedure is pending */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
        SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
                          status));
@@ -334,11 +432,6 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
        return;
       }
-
-      /* 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:
@@ -355,16 +448,24 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
                                    SILC_SKE_PK_TYPE_SILC,
                                    ctx->send_packet,
                                    context);
-       status = 0;
+
+       /* End the protocol on the next round */
+       protocol->state = SILC_PROTOCOL_STATE_END;
       } else {
        /* Finish the protocol. This verifies the Key Exchange 2 payload
-          sent by responder. */
+          sent by responder. The silc_client_protocol_ke_continue will
+          be called after the public key has been verified. */
        status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer,
-                                          ctx->verify, context, NULL, NULL);
+                                          ctx->verify, context, 
+                                          silc_client_protocol_ke_continue,
+                                          context);
       }
 
-      if (status != SILC_SKE_STATUS_OK) {
+      /* Return now if the procedure is pending */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
 
+      if (status != SILC_SKE_STATUS_OK) {
         if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) {
           client->ops->say(client, conn, 
                           "Received unsupported server %s public key",
@@ -378,14 +479,6 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
        return;
       }
-      
-      /* 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. */
-      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;
     }
     break;
 
@@ -518,6 +611,47 @@ silc_client_get_public_key_auth(SilcClient client,
   return FALSE;
 }
 
+/* Continues the connection authentication protocol. This funtion may
+   be called directly or used as SilcAskPassphrase callback. */
+
+static void 
+silc_client_conn_auth_continue(unsigned char *auth_data,
+                              uint32 auth_data_len, void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientConnAuthInternalContext *ctx = 
+    (SilcClientConnAuthInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcBuffer packet;
+  int payload_len = 0;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  payload_len = 4 + auth_data_len;
+  packet = silc_buffer_alloc(payload_len);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(payload_len),
+                    SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
+                    SILC_STR_UI_XNSTRING(auth_data, auth_data_len),
+                    SILC_STR_END);
+
+  /* Send the packet to server */
+  silc_client_packet_send(client, ctx->sock,
+                         SILC_PACKET_CONNECTION_AUTH,
+                         NULL, 0, NULL, NULL,
+                         packet->data, packet->len, TRUE);
+
+  if (auth_data) {
+    memset(auth_data, 0, auth_data_len);
+    silc_free(auth_data);
+  }
+  silc_buffer_free(packet);
+      
+  /* Next state is end of protocol */
+  protocol->state = SILC_PROTOCOL_STATE_END;
+}
+                                                   
 SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
 {
   SilcProtocol protocol = (SilcProtocol)context;
@@ -538,8 +672,6 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
        * Start protocol. We send authentication data to the server
        * to be authenticated.
        */
-      SilcBuffer packet;
-      int payload_len = 0;
       unsigned char *auth_data = NULL;
       uint32 auth_data_len = 0;
 
@@ -559,8 +691,10 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
        client->ops->say(client, conn, 
                         "Password authentication required by server %s",
                         ctx->sock->hostname);
-       auth_data = client->ops->ask_passphrase(client, conn);
-       auth_data_len = strlen(auth_data);
+       client->ops->ask_passphrase(client, conn,
+                                   silc_client_conn_auth_continue,
+                                   protocol);
+       return;
        break;
 
       case SILC_AUTH_PUBLIC_KEY:
@@ -577,29 +711,8 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
        }
       }
 
-      payload_len = 4 + auth_data_len;
-      packet = silc_buffer_alloc(payload_len);
-      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-      silc_buffer_format(packet,
-                        SILC_STR_UI_SHORT(payload_len),
-                        SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
-                        SILC_STR_UI_XNSTRING(auth_data, auth_data_len),
-                        SILC_STR_END);
-
-      /* Send the packet to server */
-      silc_client_packet_send(client, ctx->sock,
-                             SILC_PACKET_CONNECTION_AUTH,
-                             NULL, 0, NULL, NULL,
-                             packet->data, packet->len, TRUE);
-
-      if (auth_data) {
-       memset(auth_data, 0, auth_data_len);
-       silc_free(auth_data);
-      }
-      silc_buffer_free(packet);
-      
-      /* Next state is end of protocol */
-      protocol->state = SILC_PROTOCOL_STATE_END;
+      silc_client_conn_auth_continue(auth_data,
+                                    auth_data_len, protocol);
     }
     break;
 
index e55f4c66846ab75009b545fb4dc55301e37ac832..b74bd6f7b8e0f168c563b09ef89ab838aa2ee99b 100644 (file)
@@ -91,11 +91,13 @@ void silc_client_protocol_ke_send_packet(SilcSKE ske,
                                         SilcBuffer packet,
                                         SilcPacketType type,
                                         void *context);
-SilcSKEStatus silc_client_protocol_ke_verify_key(SilcSKE ske,
-                                                unsigned char *pk_data,
-                                                uint32 pk_len,
-                                                SilcSKEPKType pk_type,
-                                                void *context);
+void silc_client_protocol_ke_verify_key(SilcSKE ske,
+                                       unsigned char *pk_data,
+                                       uint32 pk_len,
+                                       SilcSKEPKType pk_type,
+                                       void *context,
+                                       SilcSKEVerifyCbCompletion completion,
+                                       void *completion_context);
 void silc_client_protocol_ke_set_keys(SilcSKE ske,
                                      SilcSocketConnection sock,
                                      SilcSKEKeyMaterial *keymat,
index 8b0462fc3e5ecdf041f78655b487983433bd09db..0c2b85f0fcadba79677928ece5c7ce7bb5d2975b 100644 (file)
@@ -84,6 +84,18 @@ typedef struct {
 
 ******************************************************************************/
 
+/* Ask passphrase callback. This is called by the application when the
+   library calls `ask_passphrase' client operation.  The callback delivers
+   the passphrase to the library. */
+typedef void (*SilcAskPassphrase)(unsigned char *passphrase,
+                                 uint32 passphrase_len,
+                                 void *context);
+
+/* Public key (or certificate) verification callback. This is called
+   by the application to indicate that the public key verification was
+   either success or failure. */
+typedef void (*SilcVerifyPublicKey)(bool success, void *context);
+
 /* SILC Client Operations. These must be implemented by the application. */
 typedef struct {
   /* Message sent to the application by library. `conn' associates the
@@ -166,17 +178,18 @@ typedef struct {
 
   /* 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, 
-                          uint32 pk_len, SilcSKEPKType pk_type);
-
-  /* Ask (interact, that is) a passphrase from user. Returns the passphrase
-     or NULL on error. */
-  unsigned char *(*ask_passphrase)(SilcClient client, 
-                                  SilcClientConnection conn);
+     the key may be saved as trusted public key for later use. The 
+     `completion' must be called after the public key has been verified. */
+  void (*verify_public_key)(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context);
+
+  /* Ask (interact, that is) a passphrase from user. The passphrase is
+     returned to the library by calling the `completion' callback with
+     the `context'. */
+  void (*ask_passphrase)(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context);
 
   /* Notifies application that failure packet was received.  This is called
      if there is some protocol active in the client.  The `protocol' is the
index 8bd412a71843864b735400baf6becb3859b4491a..694c2ac89e90b2d65201ec6967f44938aeb41472 100644 (file)
@@ -323,60 +323,51 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
   return status;
 }
 
-/* Receives Key Exchange Payload from responder consisting responders
-   public key, f, and signature. This function verifies the public key,
-   computes the secret shared key and verifies the signature. */
+/* An initiator finish final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke_payload,
-                                       SilcSKEVerifyCb verify_key,
-                                       void *verify_context,
-                                       SilcSKECb callback,
-                                       void *context)
+typedef struct {
+  SilcSKECb callback;
+  void *context;
+} *SKEInitiatorFinish;
+
+static void silc_ske_initiator_finish_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEKEPayload *payload;
-  SilcPublicKey public_key = NULL;
-  SilcInt *KEY;
+  SKEInitiatorFinish finish = (SKEInitiatorFinish)context;
+  SilcSKEKEPayload *payload = ske->ke2_payload;
   unsigned char hash[32];
   uint32 hash_len;
+  SilcPublicKey public_key = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* If the caller returns PENDING status SKE library will assume that
+     the caller will re-call this callback when it is not anymore in
+     PENDING status. */
+  if (status == SILC_SKE_STATUS_PENDING)
+    return;
 
-  /* Decode the payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &payload);
+  /* If the status is an error then the public key that was verified
+     by the caller is not authentic. */
   if (status != SILC_SKE_STATUS_OK) {
     ske->status = status;
-    return status;
+    if (finish->callback)
+      finish->callback(ske, finish->context);
+    silc_free(finish);
+    return;
   }
-  ske->ke2_payload = payload;
-
-  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
 
-  /* Compute the shared secret key */
-  KEY = silc_calloc(1, sizeof(*KEY));
-  silc_mp_init(KEY);
-  silc_mp_powm(KEY, &payload->x, ske->x, &ske->prop->group->group);
-  ske->KEY = KEY;
-
-  if (payload->pk_data) {
-    SILC_LOG_DEBUG(("Verifying public key"));
-    
-    if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
-                                    &public_key)) {
-      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-      goto err;
-    }
+  /* Decode the public key */
+  if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
+                                  &public_key)) {
+    status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+    if (finish->callback)
+      finish->callback(ske, finish->context);
+    silc_free(finish);
+    return;
   }
 
-  if (verify_key) {
-    status = (*verify_key)(ske, payload->pk_data, payload->pk_len,
-                          payload->pk_type, verify_context);
-    if (status != SILC_SKE_STATUS_OK)
-      goto err;
-
-    SILC_LOG_DEBUG(("Public key is authentic"));
-  }
+  SILC_LOG_DEBUG(("Public key is authentic"));
 
   if (payload->pk_data) {
     /* Compute the hash value */
@@ -408,11 +399,14 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
     memset(hash, 'F', hash_len);
   }
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
+  ske->status = SILC_SKE_STATUS_OK;
 
-  return status;
+  /* Call the callback. The caller may now continue the SKE protocol. */
+  if (finish->callback)
+    finish->callback(ske, finish->context);
+
+  silc_free(finish);
+  return;
 
  err:
   memset(hash, 'F', sizeof(hash));
@@ -432,6 +426,102 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
     ske->hash = NULL;
   }
 
+  if (status == SILC_SKE_STATUS_OK)
+    ske->status = SILC_SKE_STATUS_ERROR;
+
+  ske->status = status;
+
+  /* Call the callback. */
+  if (finish->callback)
+    finish->callback(ske, finish->context);
+  silc_free(finish);
+}
+
+/* Receives Key Exchange Payload from responder consisting responders
+   public key, f, and signature. This function verifies the public key,
+   computes the secret shared key and verifies the signature. 
+
+   The `callback' will be called to indicate that the caller may
+   continue with the SKE protocol.  The caller must not continue
+   before the SKE libary has called that callback.  If this function
+   returns an error the callback will not be called.  It is called
+   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
+   However, note that when the library calls the callback the ske->status
+   may be error.
+
+   This calls the `verify_key' callback to verify the received public
+   key or certificate. If the `verify_key' is provided then the remote
+   must send public key and it is considered to be an error if remote 
+   does not send its public key. If caller is performing a re-key with
+   SKE then the `verify_key' is usually not provided when it is not also
+   required for the remote to send its public key. */
+
+SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
+                                       SilcBuffer ke_payload,
+                                       SilcSKEVerifyCb verify_key,
+                                       void *verify_context,
+                                       SilcSKECb callback,
+                                       void *context)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKEKEPayload *payload;
+  SilcInt *KEY;
+  SKEInitiatorFinish finish;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Decode the payload */
+  status = silc_ske_payload_ke_decode(ske, ke_payload, &payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    return status;
+  }
+  ske->ke2_payload = payload;
+
+  if (!payload->pk_data && verify_key) {
+    SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
+                   "even though we require it"));
+    ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+    goto err;
+  }
+
+  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+
+  /* Compute the shared secret key */
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_powm(KEY, &payload->x, ske->x, &ske->prop->group->group);
+  ske->KEY = KEY;
+
+  finish = silc_calloc(1, sizeof(*finish));
+  finish->callback = callback;
+  finish->context = context;
+
+  if (verify_key) {
+    SILC_LOG_DEBUG(("Verifying public key"));
+    
+    (*verify_key)(ske, payload->pk_data, payload->pk_len,
+                 payload->pk_type, verify_context,
+                 silc_ske_initiator_finish_final, finish);
+
+    /* We will continue to the final state after the public key has
+       been verified by the caller. */
+    return SILC_SKE_STATUS_PENDING;
+  }
+
+  /* Continue to final state */
+  silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, finish);
+
+  return SILC_SKE_STATUS_OK;
+
+ err:
+  silc_ske_payload_ke_free(payload);
+  ske->ke2_payload = NULL;
+
+  silc_mp_clear(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
+
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
@@ -590,65 +680,70 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
   return status;
 }
 
-/* This function receives the Key Exchange Payload from the initiator.
-   This also performs the mutual authentication if required. Then, this 
-   function first generated a random number x, such that 1 < x < q
-   and computes f = g ^ x mod p. This then puts the result f to a Key
-   Exchange Payload. */
+/* An responder phase 2 final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke_payload,
-                                        SilcSKEVerifyCb verify_key,
-                                        void *verify_context,
-                                        SilcSKECb callback,
-                                        void *context)
+typedef struct {
+  SilcSKECb callback;
+  void *context;
+} *SKEResponderPhaseII;
+
+static void silc_ske_responder_phase2_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SKEResponderPhaseII finish = (SKEResponderPhaseII)context;
   SilcSKEKEPayload *recv_payload, *send_payload;
   SilcInt *x, f;
 
-  SILC_LOG_DEBUG(("Start"));
+  recv_payload = ske->ke1_payload;
 
-  /* Decode Key Exchange Payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
+  /* If the caller returns PENDING status SKE library will assume that
+     the caller will re-call this callback when it is not anymore in
+     PENDING status. */
+  if (status == SILC_SKE_STATUS_PENDING)
+    return;
+
+  /* If the status is an error then the public key that was verified
+     by the caller is not authentic. */
   if (status != SILC_SKE_STATUS_OK) {
     ske->status = status;
-    return status;
+    if (finish->callback)
+      finish->callback(ske, finish->context);
+    silc_free(finish);
+    return;
   }
 
-  ske->ke1_payload = recv_payload;
-
-  /* Verify the received public key and verify the signature if we are
-     doing mutual authentication. */
+  /* The public key verification was performed only if the Mutual
+     Authentication flag is set. */
   if (ske->start_payload && 
       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     SilcPublicKey public_key = NULL;
     unsigned char hash[32];
     uint32 hash_len;
 
-    SILC_LOG_DEBUG(("We are doing mutual authentication"));
-    SILC_LOG_DEBUG(("Verifying public key"));
-    
+    /* Decode the public key */
     if (!silc_pkcs_public_key_decode(recv_payload->pk_data, 
                                     recv_payload->pk_len, 
                                     &public_key)) {
-      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-      return status;
-    }
-
-    if (verify_key) {
-      status = (*verify_key)(ske, recv_payload->pk_data, recv_payload->pk_len,
-                            recv_payload->pk_type, verify_context);
-      if (status != SILC_SKE_STATUS_OK)
-       return status;
+      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      if (finish->callback)
+       finish->callback(ske, finish->context);
+      silc_free(finish);
+      return;
     }
 
     SILC_LOG_DEBUG(("Public key is authentic"));
 
     /* Compute the hash value */
     status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
-    if (status != SILC_SKE_STATUS_OK)
-      return status;
+    if (status != SILC_SKE_STATUS_OK) {
+      ske->status = status;
+      if (finish->callback)
+       finish->callback(ske, finish->context);
+      silc_free(finish);
+      return;
+    }
 
     SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
     
@@ -660,8 +755,11 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
       
       SILC_LOG_DEBUG(("Signature don't match"));
       
-      status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-      return status;
+      ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      if (finish->callback)
+       finish->callback(ske, finish->context);
+      silc_free(finish);
+      return;
     }
     
     SILC_LOG_DEBUG(("Signature is Ok"));
@@ -680,7 +778,11 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
   if (status != SILC_SKE_STATUS_OK) {
     silc_mp_clear(x);
     silc_free(x);
-    return status;
+    ske->status = status;
+    if (finish->callback)
+      finish->callback(ske, finish->context);
+    silc_free(finish);
+    return;
   }
 
   SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
@@ -696,11 +798,89 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
   ske->x = x;
   ske->ke2_payload = send_payload;
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
+  /* Call the callback. The caller may now continue with the SKE protocol. */
+  ske->status = SILC_SKE_STATUS_OK;
+  if (finish->callback)
+    finish->callback(ske, finish->context);
+  silc_free(finish);
+}
 
-  return status;
+/* This function receives the Key Exchange Payload from the initiator.
+   This also performs the mutual authentication if required. Then, this 
+   function first generated a random number x, such that 1 < x < q
+   and computes f = g ^ x mod p. This then puts the result f to a Key
+   Exchange Payload. 
+
+   The `callback' will be called to indicate that the caller may
+   continue with the SKE protocol.  The caller must not continue
+   before the SKE libary has called that callback.  If this function
+   returns an error the callback will not be called.  It is called
+   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
+   However, note that when the library calls the callback the ske->status
+   may be error.
+
+   This calls the `verify_key' callback to verify the received public
+   key or certificate if the Mutual Authentication flag is set. If the
+   `verify_key' is provided then the remote must send public key and it
+   is considered to be an error if remote does not send its public key. */
+
+SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
+                                        SilcBuffer ke_payload,
+                                        SilcSKEVerifyCb verify_key,
+                                        void *verify_context,
+                                        SilcSKECb callback,
+                                        void *context)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKEKEPayload *recv_payload;
+  SKEResponderPhaseII finish;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Decode Key Exchange Payload */
+  status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    return status;
+  }
+
+  ske->ke1_payload = recv_payload;
+
+  finish = silc_calloc(1, sizeof(*finish));
+  finish->callback = callback;
+  finish->context = context;
+
+  /* Verify the received public key and verify the signature if we are
+     doing mutual authentication. */
+  if (ske->start_payload && 
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+
+    SILC_LOG_DEBUG(("We are doing mutual authentication"));
+    
+    if (!recv_payload->pk_data && verify_key) {
+      SILC_LOG_DEBUG(("Remote end did not send its public key (or "
+                     "certificate), even though we require it"));
+      ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+      return status;
+    }
+
+    if (verify_key) {
+      SILC_LOG_DEBUG(("Verifying public key"));
+
+      (*verify_key)(ske, recv_payload->pk_data, recv_payload->pk_len,
+                   recv_payload->pk_type, verify_context,
+                   silc_ske_responder_phase2_final, finish);
+
+      /* We will continue to the final state after the public key has
+        been verified by the caller. */
+      return SILC_SKE_STATUS_PENDING;
+    }
+  }
+
+  /* Continue to final state */
+  silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, finish);
+
+  return SILC_SKE_STATUS_OK;
 }
 
 /* This functions generates the secret key KEY = e ^ x mod p, and, a hash
index 6774ea7fe726f8bb262a44eee3462ee42d85f2a3..1828d10fd49b5e31dd52124f070e29966df2ebb4 100644 (file)
@@ -46,15 +46,35 @@ typedef void (*SilcSKESendPacketCb)(SilcSKE ske, SilcBuffer packet,
 
 /* Generic SKE callback function. This is called in various SKE
    routines. The SilcSKE object sent as argument provides all the data
-   callers routine might need (payloads etc). */
+   callers routine might need (payloads etc). This is usually called
+   to indicate that the application may continue the execution of the
+   SKE protocol. The application should check the ske->status in this
+   callback function. */
 typedef void (*SilcSKECb)(SilcSKE ske, void *context);
 
-/* Callback function used to verify the received public key. */
-typedef SilcSKEStatus (*SilcSKEVerifyCb)(SilcSKE ske, 
-                                        unsigned char *pk_data,
-                                        uint32 pk_len,
-                                        SilcSKEPKType pk_type,
-                                        void *context);
+/* Completion callback that will be called when the public key
+   has been verified.  The `status' will indicate whether the public
+   key were trusted or not. If the `status' is PENDING then the status
+   is not considered to be available at this moment. In this case the
+   SKE libary will assume that the caller will call this callback again
+   when the status is available. */
+typedef void (*SilcSKEVerifyCbCompletion)(SilcSKE ske,
+                                         SilcSKEStatus status,
+                                         void *context);
+
+/* Callback function used to verify the received public key or certificate. 
+   The verification process is most likely asynchronous. That's why the
+   application must call the `completion' callback when the verification
+   process has been completed. The library then calls the user callback
+   (SilcSKECb), if it was provided for the function that takes this callback
+   function as argument, to indicate that the SKE protocol may continue. */
+typedef void (*SilcSKEVerifyCb)(SilcSKE ske, 
+                               unsigned char *pk_data,
+                               uint32 pk_len,
+                               SilcSKEPKType pk_type,
+                               void *context,
+                               SilcSKEVerifyCbCompletion completion,
+                               void *completion_context);
 
 /* Context passed to key material processing function. The function
    returns the processed key material into this structure. */
index f6ef92128c02d1ab6d694330f88750c261d013e8..c2f85c9c5b56b3f77f6a9bde73148589ce080f1d 100644 (file)
@@ -36,6 +36,8 @@ typedef enum {
   SILC_SKE_STATUS_INCORRECT_SIGNATURE    = 9,
   SILC_SKE_STATUS_BAD_VERSION            = 10,
 
+  SILC_SKE_STATUS_PENDING,
+  SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED,
   SILC_SKE_STATUS_KEY_EXCHANGE_NOT_ACTIVE,
   SILC_SKE_STATUS_BAD_RESERVED_FIELD,
   SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH,