+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.
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
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);
}
#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;
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,
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);
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
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)
#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. */
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);
/* 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) {
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
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,
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. */
SilcServerKEInternalContext *ctx =
(SilcServerKEInternalContext *)protocol->context;
SilcServer server = (SilcServer)ctx->server;
- SilcSKEStatus status = 0;
+ SilcSKEStatus status = SILC_SKE_STATUS_OK;
SILC_LOG_DEBUG(("Start"));
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));
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));
/* 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 {
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));
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));
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;
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);
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);
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. */
{
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);
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 */
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. */
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. */
(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"));
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));
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
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));
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:
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",
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;
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;
* 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;
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:
}
}
- 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;
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,
******************************************************************************/
+/* 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
/* 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
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 */
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));
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;
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)"));
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"));
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"));
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
/* 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. */
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,