Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2001 - 2006 Pekka Riikonen
+ Copyright (C) 2001 - 2007 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
struct SilcClientKeyAgreementStruct {
SilcClient client; /* Client */
SilcClientConnection conn; /* Server connection */
+ SilcClientListener listener; /* Listener */
SilcKeyAgreementCallback completion; /* Key agreement completion */
void *context; /* User context */
- SilcClientConnectionParams params; /* Connection parameters */
- SilcPublicKey public_key; /* Responder public key */
- SilcPrivateKey private_key; /* Responder private key */
- SilcNetListener tcp_listener; /* TCP listener */
- SilcPacketStream udp_listener; /* UDP listener */
- SilcPacketStream stream; /* Remote connection (TCP or UDP) */
- SilcAsyncOperation op; /* SKE operation */
- SilcSKE ske; /* SKE */
+ SilcAsyncOperation op; /* Async operation, initiator */
};
/************************ Static utility functions **************************/
{
SilcClientKeyAgreement ke = client_entry->internal.ke;
+ silc_client_listener_free(ke->listener);
silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
-
if (ke->op)
silc_async_abort(ke->op, NULL, NULL);
- if (ke->ske)
- silc_ske_free(ke->ske);
- if (ke->tcp_listener)
- silc_net_close_listener(ke->tcp_listener);
- silc_packet_stream_destroy(ke->stream);
- silc_packet_stream_destroy(ke->udp_listener);
-
client_entry->internal.ke = NULL;
client_entry->internal.prv_resp = FALSE;
silc_client_unref_client(client, conn, client_entry);
-
silc_free(ke);
}
SilcClientEntry client_entry = context;
SilcClientKeyAgreement ke = client_entry->internal.ke;
+ if (!ke)
+ return;
+
SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
ke->completion(ke->client, ke->conn, client_entry,
SILC_FSM_CALL_CONTINUE(context);
}
-/* Called after application has verified remote host's public key. Responder
- function. */
-
-static void silc_client_keyagr_verify_key_cb(SilcBool success, void *context)
-{
- VerifyKeyContext verify = context;
-
- /* 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);
-}
-
-/* Verify remote host's public key. Responder function. */
-
-static void silc_client_keyagr_verify_key(SilcSKE ske,
- SilcPublicKey public_key,
- void *context,
- SilcSKEVerifyCbCompletion completion,
- void *completion_context)
-{
- SilcClientEntry client_entry = context;
- SilcClientKeyAgreement ke = client_entry->internal.ke;
- SilcClientConnection conn = ke->conn;
- SilcClient client = conn->client;
- VerifyKeyContext verify;
-
- /* If we provided repository for SKE and we got here the key was not
- found from the repository. */
- if (ke->params.repository && !ke->params.verify_notfound) {
- completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
- completion_context);
- return;
- }
-
- SILC_LOG_DEBUG(("Verify remote public key"));
-
- verify = silc_calloc(1, sizeof(*verify));
- if (!verify) {
- completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
- completion_context);
- return;
- }
- verify->ske = ske;
- verify->completion = completion;
- verify->completion_context = completion_context;
-
- /* Verify public key in application */
- client->internal->ops->verify_public_key(client, conn,
- SILC_CONN_CLIENT, public_key,
- silc_client_keyagr_verify_key_cb,
- verify);
-}
-
-/* Key exchange protocol completion callback. Responder function. */
-
-static void silc_client_keyagr_completion(SilcSKE ske,
- SilcSKEStatus status,
- SilcSKESecurityProperties prop,
- SilcSKEKeyMaterial keymat,
- SilcSKERekeyMaterial rekey,
- void *context)
-{
- SilcClientEntry client_entry = context;
- SilcClientKeyAgreement ke = client_entry->internal.ke;
- SilcClientConnection conn = ke->conn;
- SilcClient client = conn->client;
-
- if (status != SILC_SKE_STATUS_OK) {
- /* Key exchange failed */
- ke->completion(client, conn, client_entry,
- status == SILC_SKE_STATUS_TIMEOUT ?
- SILC_KEY_AGREEMENT_TIMEOUT :
- SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
- silc_client_keyagr_free(client, conn, client_entry);
- return;
- }
-
- /* Returns the negotiated key material to application. Key agreement
- was successful. */
- ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_OK,
- keymat, ke->context);
-
- silc_client_keyagr_free(client, conn, client_entry);
-}
-
-/* Starts key agreement as responder. */
-
-static void silc_client_process_key_agreement(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry)
-{
- SilcClientKeyAgreement ke = client_entry->internal.ke;
- SilcSKEParamsStruct params;
+/* Key exchange completion callback. Called after connected to remote host
+ and performed key exchange, when we are initiator. As responder, this is
+ called after the remote has connected to us and have performed the key
+ exchange. */
- SILC_LOG_DEBUG(("Processing key agrement %p session", ke));
-
- /* Allocate SKE */
- ke->ske = silc_ske_alloc(client->rng, conn->internal->schedule,
- ke->params.repository, ke->public_key,
- ke->private_key, client_entry);
- if (!ke->ske) {
- ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
- NULL, ke->context);
- silc_client_keyagr_free(client, conn, client_entry);
- return;
- }
-
- /* Set SKE parameters */
- params.version = client->internal->silc_client_version;
- params.flags = SILC_SKE_SP_FLAG_MUTUAL;
- if (ke->params.udp) {
- params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
- params.session_port = ke->params.local_port;
- }
-
- silc_ske_set_callbacks(ke->ske, silc_client_keyagr_verify_key,
- silc_client_keyagr_completion, client_entry);
-
- /* Start key exchange as responder */
- ke->op = silc_ske_responder(ke->ske, ke->stream, ¶ms);
- if (!ke->op) {
- ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
- NULL, ke->context);
- silc_client_keyagr_free(client, conn, client_entry);
- }
-}
-
-/* TCP network listener callback. Accepts new key agreement connection.
- Responder function. */
-
-static void silc_client_tcp_accept(SilcNetStatus status,
- SilcStream stream,
- void *context)
-{
- SilcClientEntry client_entry = context;
- SilcClientKeyAgreement ke = client_entry->internal.ke;
-
- /* Create packet stream */
- ke->stream = silc_packet_stream_create(ke->client->internal->packet_engine,
- ke->conn->internal->schedule, stream);
- if (!ke->stream) {
- silc_stream_destroy(stream);
- return;
- }
-
- /* Process session */
- silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
-}
-
-/* UDP network listener callback. Accepts new key agreement session.
- Responder function. */
-
-static SilcBool silc_client_udp_accept(SilcPacketEngine engine,
- SilcPacketStream stream,
- SilcPacket packet,
- void *callback_context,
- void *stream_context)
-{
- SilcClientEntry client_entry = callback_context;
- SilcClientKeyAgreement ke = client_entry->internal.ke;
- SilcUInt16 port;
- const char *ip;
-
- /* We want only key exchange packet. Eat other packets so that default
- packet callback doesn't get them. */
- if (packet->type != SILC_PACKET_KEY_EXCHANGE) {
- silc_packet_free(packet);
- return TRUE;
- }
-
- /* Create packet stream for this remote UDP session */
- if (!silc_packet_get_sender(packet, &ip, &port)) {
- silc_packet_free(packet);
- return TRUE;
- }
- ke->stream = silc_packet_stream_add_remote(stream, ip, port, packet);
- if (!ke->stream) {
- silc_packet_free(packet);
- return TRUE;
- }
-
- /* Process session */
- silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
- return TRUE;
-}
-
-/* Client connect completion callback. Initiator function. */
-
-static void silc_client_keyagr_perform_cb(SilcClient client,
+static void silc_client_keyagr_completion(SilcClient client,
SilcClientConnection conn,
SilcClientConnectionStatus status,
SilcStatus error,
break;
}
- /* Close the created connection */
+ /* Close the connection */
if (conn)
silc_client_close_connection(ke->client, conn);
silc_client_keyagr_free(ke->client, ke->conn, client_entry);
}
-/* Packet stream callbacks */
-static SilcPacketCallbacks silc_client_keyagr_stream_cb =
-{
- silc_client_udp_accept, NULL, NULL
-};
-
/*************************** Key Agreement API ******************************/
/* Sends key agreement packet to remote client. If IP addresses are provided
SilcBuffer buffer;
SilcUInt16 port = 0, protocol = 0;
char *local_ip = NULL;
- SilcStream stream;
SILC_LOG_DEBUG(("Sending key agreement"));
if (!client_entry)
return;
+ if (conn->internal->disconnected)
+ return;
if (client_entry->internal.ke) {
completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED,
return;
}
- /* If local IP is provided, create listener */
+ /* If local IP is provided, create listener. If this is not provided,
+ we'll just send empty key agreement payload */
if (params && (params->local_ip || params->bind_ip)) {
ke = silc_calloc(1, sizeof(*ke));
if (!ke) {
return;
}
- /* Create network listener */
- if (params->udp) {
- /* UDP listener */
- stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
- params->local_ip, params->local_port,
- NULL, 0, conn->internal->schedule);
- ke->udp_listener =
- silc_packet_stream_create(client->internal->packet_engine,
- conn->internal->schedule, stream);
- if (!ke->udp_listener) {
- client->internal->ops->say(
- client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Cannot create UDP listener on %s on port %d: %s",
- params->bind_ip ? params->bind_ip :
- params->local_ip, params->local_port, strerror(errno));
- completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
- NULL, context);
- if (stream)
- silc_stream_destroy(stream);
- silc_free(ke);
- return;
- }
- silc_packet_stream_link(ke->udp_listener,
- &silc_client_keyagr_stream_cb,
- client_entry, 1000000,
- SILC_PACKET_ANY, -1);
-
- port = params->local_port;
- if (!port) {
- /* Get listener port */
- int sock;
- silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
- port = silc_net_get_local_port(sock);
- }
- } else {
- /* TCP listener */
- ke->tcp_listener =
- silc_net_tcp_create_listener(params->bind_ip ?
- (const char **)¶ms->bind_ip :
- (const char **)¶ms->local_ip,
- 1, params->local_port, FALSE, FALSE,
- conn->internal->schedule,
- silc_client_tcp_accept,
- client_entry);
- if (!ke->tcp_listener) {
- client->internal->ops->say(
- client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Cannot create listener on %s on port %d: %s",
- params->bind_ip ? params->bind_ip :
- params->local_ip, params->local_port, strerror(errno));
- completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
- NULL, context);
- silc_free(ke);
- return;
- }
-
- port = params->local_port;
- if (!port) {
- /* Get listener port */
- SilcUInt16 *ports;
- ports = silc_net_listener_get_port(ke->tcp_listener, NULL);
- port = ports[0];
- silc_free(ports);
- }
+ /* Create listener */
+ ke->listener = silc_client_listener_add(client, conn->internal->schedule,
+ params, public_key, private_key,
+ silc_client_keyagr_completion,
+ client_entry);
+ if (!ke->listener) {
+ completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+ NULL, context);
+ return;
}
local_ip = params->local_ip;
ke->conn = conn;
ke->completion = completion;
ke->context = context;
- ke->params = *params;
- ke->public_key = public_key;
- ke->private_key = private_key;
silc_client_ref_client(client, conn, client_entry);
client_entry->internal.ke = ke;
client_entry->internal.prv_resp = TRUE;
params->no_authentication = TRUE;
/* Connect to the remote client. Performs key exchange automatically. */
- if (!silc_client_connect_to_client(client, params, public_key,
- private_key, hostname, port,
- silc_client_keyagr_perform_cb,
- client_entry)) {
+ ke->op = silc_client_connect_to_client(client, params, public_key,
+ private_key, hostname, port,
+ silc_client_keyagr_completion,
+ client_entry);
+ if (!ke->op) {
completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
NULL, context);
silc_client_keyagr_free(client, conn, client_entry);
params->no_authentication = TRUE;
/* Perform key exchange protocol */
- if (!silc_client_key_exchange(client, params, public_key,
- private_key, stream, SILC_CONN_CLIENT,
- silc_client_keyagr_perform_cb,
- client_entry)) {
+ ke->op = silc_client_key_exchange(client, params, public_key,
+ private_key, stream, SILC_CONN_CLIENT,
+ silc_client_keyagr_completion,
+ client_entry);
+ if (!ke->op) {
completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
NULL, context);
silc_client_keyagr_free(client, conn, client_entry);
/* Check whether we know this client already */
remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
- if (!remote_client || !remote_client->nickname[0]) {
+ if (!remote_client || !remote_client->internal.valid) {
/** Resolve client info */
silc_client_unref_client(client, conn, remote_client);
SILC_FSM_CALL(silc_client_get_client_by_id_resolve(