#include "silcclient.h"
#include "client_internal.h"
-/************************** Types and definitions ***************************/
-
-/* Public key verification context */
-typedef struct {
- SilcSKE ske;
- SilcSKEVerifyCbCompletion completion;
- void *completion_context;
-} *VerifyKeyContext;
-
-
/************************ Static utility functions **************************/
/* Callback called after connected to remote host */
SilcClientConnection conn = silc_fsm_get_context(fsm);
SilcClient client = conn->client;
+ conn->internal->op = NULL;
if (conn->internal->verbose) {
switch (status) {
case SILC_NET_OK:
/* Notify application of failure */
SILC_LOG_DEBUG(("Connecting failed"));
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0,
- NULL, conn->context);
+ NULL, conn->callback_context);
silc_fsm_next(fsm, silc_client_st_connect_error);
SILC_FSM_CALL_CONTINUE(fsm);
return;
SilcCipher send_key, receive_key;
SilcHmac hmac_send, hmac_receive;
+ conn->internal->op = NULL;
if (status != SILC_SKE_STATUS_OK) {
/* Key exchange failed */
SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
conn->remote_host,
silc_ske_map_status(status));
+ silc_ske_free_rekey_material(rekey);
+
silc_fsm_next(fsm, silc_client_st_connect_error);
SILC_FSM_CALL_CONTINUE(fsm);
return;
SILC_LOG_DEBUG(("Setting keys into use"));
- /* Set the keys into use. Data will be encrypted after this. */
+ /* Allocate the cipher and HMAC contexts */
if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
&hmac_send, &hmac_receive, &conn->internal->hash)) {
/* Error setting keys */
conn->remote_host);
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
- conn->context);
+ conn->callback_context);
+
+ silc_ske_free_rekey_material(rekey);
silc_fsm_next(fsm, silc_client_st_connect_error);
SILC_FSM_CALL_CONTINUE(fsm);
return;
}
- silc_packet_set_ciphers(conn->stream, send_key, receive_key);
- silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
+ /* Set the keys into the packet stream. After this call packets will be
+ encrypted with these keys. */
+ if (!silc_packet_set_keys(conn->stream, send_key, receive_key, hmac_send,
+ hmac_receive, FALSE)) {
+ /* Error setting keys */
+ SILC_LOG_DEBUG(("Could not set keys into use"));
+
+ if (conn->internal->verbose)
+ client->internal->ops->say(
+ client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "Error during key exchange with %s: cannot use keys",
+ conn->remote_host);
+
+ conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
+ conn->callback_context);
+
+ silc_ske_free_rekey_material(rekey);
+
+ silc_fsm_next(fsm, silc_client_st_connect_error);
+ SILC_FSM_CALL_CONTINUE(fsm);
+ return;
+ }
conn->internal->rekey = rekey;
+ SILC_LOG_DEBUG(("Key Exchange completed"));
+
/* Key exchange done */
SILC_FSM_CALL_CONTINUE(fsm);
}
+/* Rekey protocol completion callback */
+
+static void silc_client_rekey_completion(SilcSKE ske,
+ SilcSKEStatus status,
+ SilcSKESecurityProperties prop,
+ SilcSKEKeyMaterial keymat,
+ SilcSKERekeyMaterial rekey,
+ void *context)
+{
+ SilcFSMThread fsm = context;
+ SilcClientConnection conn = silc_fsm_get_context(fsm);
+ SilcClient client = conn->client;
+
+ conn->internal->op = NULL;
+ if (status != SILC_SKE_STATUS_OK) {
+ /* Rekey failed */
+ SILC_LOG_DEBUG(("Error during rekey with %s: %s (%d)",
+ conn->remote_host, silc_ske_map_status(status), status));
+
+ if (conn->internal->verbose)
+ client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "Error during rekey with %s: %s",
+ conn->remote_host,
+ silc_ske_map_status(status));
+
+ silc_fsm_finish(fsm);
+ return;
+ }
+
+ silc_ske_free_rekey_material(conn->internal->rekey);
+ conn->internal->rekey = rekey;
+
+ SILC_LOG_DEBUG(("Rekey completed"));
+
+ /* Rekey done */
+ silc_fsm_finish(fsm);
+}
+
/* Callback called by application to return authentication data */
static void silc_client_connect_auth_method(SilcBool success,
SilcClientConnection conn = silc_fsm_get_context(fsm);
SilcClient client = conn->client;
+ conn->internal->op = NULL;
silc_connauth_free(connauth);
if (!success) {
"Authentication failed");
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
- conn->context);
+ conn->callback_context);
silc_fsm_next(fsm, silc_client_st_connect_error);
}
/*************************** Connect remote host ****************************/
+/* Connection timeout callback */
+
+SILC_TASK_CALLBACK(silc_client_connect_timeout)
+{
+ SilcClientConnection conn = context;
+ SilcClient client = conn->client;
+
+ SILC_LOG_DEBUG(("Connection timeout"));
+
+ conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_TIMEOUT, 0, NULL,
+ conn->callback_context);
+
+ silc_fsm_next(&conn->internal->event_thread, silc_client_st_connect_error);
+ silc_fsm_continue_sync(&conn->internal->event_thread);
+}
+
/* Creates a connection to remote host */
SILC_FSM_STATE(silc_client_st_connect)
/** Connect */
silc_fsm_next(fsm, silc_client_st_connect_set_stream);
+ /* Add connection timeout */
+ if (conn->internal->params.timeout_secs)
+ silc_schedule_task_add_timeout(conn->internal->schedule,
+ silc_client_connect_timeout, conn,
+ conn->internal->params.timeout_secs, 0);
+
if (conn->internal->params.udp) {
SilcStream stream;
/** IP address not given */
SILC_LOG_ERROR(("Local UDP IP address not specified"));
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
- conn->context);
+ conn->callback_context);
silc_fsm_next(fsm, silc_client_st_connect_error);
return SILC_FSM_CONTINUE;
}
/* Connect (UDP) */
- stream = silc_net_udp_connect(conn->internal->params.local_ip,
+ stream = silc_net_udp_connect(conn->internal->params.bind_ip ?
+ conn->internal->params.bind_ip :
+ conn->internal->params.local_ip,
conn->internal->params.local_port,
conn->remote_host, conn->remote_port,
conn->internal->schedule);
stream, fsm));
} else {
/* Connect (TCP) */
- SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host,
+ SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect(
+ NULL, conn->remote_host,
conn->remote_port,
conn->internal->schedule,
silc_client_connect_callback, fsm));
SilcClientConnection conn = fsm_context;
SilcClient client = conn->client;
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_connect_error);
+ return SILC_FSM_CONTINUE;
+ }
+
/* Create packet stream */
conn->stream = silc_packet_stream_create(client->internal->packet_engine,
conn->internal->schedule,
/** Cannot create packet stream */
SILC_LOG_DEBUG(("Could not create packet stream"));
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
- conn->context);
+ conn->callback_context);
silc_fsm_next(fsm, silc_client_st_connect_error);
return SILC_FSM_CONTINUE;
}
if (!conn->internal->ske) {
/** Out of memory */
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
- conn->context);
+ conn->callback_context);
silc_fsm_next(fsm, silc_client_st_connect_error);
return SILC_FSM_CONTINUE;
}
/** Run key exchange (TCP) */
silc_fsm_next(fsm, silc_client_st_connect_auth);
- SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream,
- ¶ms, NULL));
+ SILC_FSM_CALL(conn->internal->op = silc_ske_initiator(conn->internal->ske,
+ conn->stream,
+ ¶ms, NULL));
}
/* For UDP/IP connections, set up the UDP session after successful key
SILC_LOG_DEBUG(("Setup UDP SILC session"));
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_connect_error);
+ return SILC_FSM_CONTINUE;
+ }
+
/* Create new UDP stream */
prop = silc_ske_get_security_properties(conn->internal->ske);
stream = silc_net_udp_connect(conn->internal->params.local_ip,
if (!stream) {
/** Cannot create UDP stream */
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
- conn->context);
+ conn->callback_context);
silc_fsm_next(fsm, silc_client_st_connect_error);
return SILC_FSM_CONTINUE;
}
SILC_LOG_DEBUG(("Get authentication data"));
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_connect_error);
+ return SILC_FSM_CONTINUE;
+ }
+
silc_fsm_next(fsm, silc_client_st_connect_auth_start);
/* If authentication data not provided, ask from application */
SILC_LOG_DEBUG(("Starting connection authentication protocol"));
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_connect_error);
+ return SILC_FSM_CONTINUE;
+ }
+
/* Allocate connection authentication protocol */
connauth = silc_connauth_alloc(conn->internal->schedule,
conn->internal->ske,
- client->internal->params->rekey_secs);
+ conn->internal->params.rekey_secs);
if (!connauth) {
/** Out of memory */
conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
- conn->context);
+ conn->callback_context);
silc_fsm_next(fsm, silc_client_st_connect_error);
return SILC_FSM_CONTINUE;
}
/** Start connection authentication */
silc_fsm_next(fsm, silc_client_st_connected);
- SILC_FSM_CALL(silc_connauth_initiator(connauth, SILC_CONN_CLIENT,
+ SILC_FSM_CALL(conn->internal->op = silc_connauth_initiator(
+ connauth, SILC_CONN_CLIENT,
conn->internal->params.auth_method,
conn->internal->params.auth,
conn->internal->params.auth_len,
SilcClientConnection conn = fsm_context;
SilcClient client = conn->client;
+ silc_ske_free(conn->internal->ske);
+ conn->internal->ske = NULL;
+
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_connect_error);
+ return SILC_FSM_CONTINUE;
+ }
+
SILC_LOG_DEBUG(("Connection established"));
+ /* Install rekey timer */
+ silc_schedule_task_add_timeout(conn->internal->schedule,
+ silc_client_rekey_timer, conn,
+ conn->internal->params.rekey_secs, 0);
+
/* If we connected to server, now register to network. */
if (conn->type == SILC_CONN_SERVER &&
!conn->internal->params.no_authentication) {
return SILC_FSM_CONTINUE;
}
+ silc_schedule_task_del_by_all(conn->internal->schedule, 0,
+ silc_client_connect_timeout, conn);
+
/* Call connection callback */
conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
- conn->context);
+ conn->callback_context);
return SILC_FSM_FINISH;
}
SILC_FSM_STATE(silc_client_st_connect_error)
{
+ SilcClientConnection conn = fsm_context;
+
+ if (conn->internal->ske) {
+ silc_ske_free(conn->internal->ske);
+ conn->internal->ske = NULL;
+ }
- /* XXX */
- /* Close connection */
+ /* Signal to close connection */
+ if (!conn->internal->disconnected) {
+ conn->internal->disconnected = TRUE;
+ SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+ }
+
+ silc_schedule_task_del_by_all(conn->internal->schedule, 0,
+ silc_client_connect_timeout, conn);
return SILC_FSM_FINISH;
}
+
+/****************************** Connect rekey *******************************/
+
+/* Connection rekey timer callback */
+
+SILC_TASK_CALLBACK(silc_client_rekey_timer)
+{
+ SilcClientConnection conn = context;
+
+ /* Signal to start rekey */
+ conn->internal->rekey_responder = FALSE;
+ conn->internal->rekeying = TRUE;
+ SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+
+ /* Reinstall rekey timer */
+ silc_schedule_task_add_timeout(conn->internal->schedule,
+ silc_client_rekey_timer, conn,
+ conn->internal->params.rekey_secs, 0);
+}
+
+/* Performs rekey */
+
+SILC_FSM_STATE(silc_client_st_rekey)
+{
+ SilcClientConnection conn = fsm_context;
+ SilcClient client = conn->client;
+
+ SILC_LOG_DEBUG(("Rekey"));
+
+ if (conn->internal->disconnected)
+ return SILC_FSM_FINISH;
+
+ /* Allocate SKE */
+ conn->internal->ske =
+ silc_ske_alloc(client->rng, conn->internal->schedule,
+ conn->internal->params.repository,
+ conn->public_key, conn->private_key, fsm);
+ if (!conn->internal->ske)
+ return SILC_FSM_FINISH;
+
+ /* Set SKE callbacks */
+ silc_ske_set_callbacks(conn->internal->ske, NULL,
+ silc_client_rekey_completion, fsm);
+
+ /** Perform rekey */
+ if (!conn->internal->rekey_responder)
+ SILC_FSM_CALL(conn->internal->op = silc_ske_rekey_initiator(
+ conn->internal->ske,
+ conn->stream,
+ conn->internal->rekey));
+ else
+ SILC_FSM_CALL(conn->internal->op = silc_ske_rekey_responder(
+ conn->internal->ske,
+ conn->stream,
+ conn->internal->rekey));
+}