From 81f0294584576e6cff50ebcfd7160017dbf7ba97 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Thu, 7 Dec 2006 17:01:49 +0000 Subject: [PATCH] More client library rewrites (key agreement, plus other). --- lib/silcclient/Makefile.ad | 1 + lib/silcclient/client.c | 16 +- lib/silcclient/client.h | 4 +- lib/silcclient/client_connect.c | 45 +- lib/silcclient/client_entry.c | 6 + lib/silcclient/client_internal.h | 11 +- lib/silcclient/client_keyagr.c | 942 ++++++++++++------------- lib/silcclient/client_keyagr.h | 26 + lib/silcclient/client_notify.c | 12 +- lib/silcclient/client_prvmsg.c | 127 ++-- lib/silcclient/client_register.c | 10 +- lib/silcclient/command.c | 6 +- lib/silcclient/command_reply.c | 8 +- lib/silcclient/silcclient.h | 279 +++----- lib/silcclient/tests/test_silcclient.c | 5 +- 15 files changed, 738 insertions(+), 760 deletions(-) create mode 100644 lib/silcclient/client_keyagr.h diff --git a/lib/silcclient/Makefile.ad b/lib/silcclient/Makefile.ad index 1263c327..58912a13 100644 --- a/lib/silcclient/Makefile.ad +++ b/lib/silcclient/Makefile.ad @@ -28,6 +28,7 @@ libsilcclient_la_SOURCES = \ client_register.c \ client_notify.c \ client_attrs.c \ + client_keyagr.c \ command.c \ command_reply.c diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index bd354495..9354d28d 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -281,8 +281,8 @@ SILC_FSM_STATE(silc_client_connection_st_packet) break; case SILC_PACKET_KEY_AGREEMENT: - /* Key agreement */ - // silc_client_key_agreement(client, conn, packet); + /** Key agreement */ + silc_fsm_next(fsm, silc_client_key_agreement); break; case SILC_PACKET_COMMAND: @@ -388,8 +388,10 @@ SILC_FSM_STATE(silc_client_disconnect) silc_packet_free(packet); /* Signal to close connection */ - conn->internal->disconnected = TRUE; - SILC_FSM_SEMA_POST(&conn->internal->wait_event); + if (!conn->internal->disconnected) { + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + } return SILC_FSM_FINISH; } @@ -682,8 +684,10 @@ void silc_client_close_connection(SilcClient client, SILC_LOG_DEBUG(("Closing connection %p", conn)); /* Signal to close connection */ - conn->internal->disconnected = TRUE; - SILC_FSM_SEMA_POST(&conn->internal->wait_event); + if (!conn->internal->disconnected) { + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + } } #if 0 diff --git a/lib/silcclient/client.h b/lib/silcclient/client.h index d60b740a..53a5550a 100644 --- a/lib/silcclient/client.h +++ b/lib/silcclient/client.h @@ -57,8 +57,8 @@ typedef struct SilcClientEntryInternalStruct { unsigned int valid : 1; /* FALSE if this entry is not valid */ unsigned int resolving : 1; /* TRUE when entry is being resolved */ unsigned int generated : 1; /* TRUE if library generated `key' */ - unsigned int prv_resp : 1; /* TRUE if private message key indicator - has been received (responder). */ + unsigned int prv_resp : 1; /* TRUE if we are responder when using + private message keys. */ SilcUInt16 resolve_cmd_ident; /* Command identifier when resolving */ SilcAtomic8 refcnt; /* Reference counter */ } SilcClientEntryInternal; diff --git a/lib/silcclient/client_connect.c b/lib/silcclient/client_connect.c index 4aff0a6b..7c50d2b9 100644 --- a/lib/silcclient/client_connect.c +++ b/lib/silcclient/client_connect.c @@ -23,13 +23,6 @@ /************************** 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 */ @@ -41,6 +34,7 @@ static void silc_client_connect_callback(SilcNetStatus status, 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: @@ -172,6 +166,7 @@ static void silc_client_ke_completion(SilcSKE ske, 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)", @@ -266,6 +261,22 @@ static void silc_client_connect_auth_completion(SilcConnAuth connauth, SILC_FSM_CALL_CONTINUE(fsm); } +/* 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); +} + /*************************** Connect remote host ****************************/ /* Creates a connection to remote host */ @@ -281,6 +292,12 @@ 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; @@ -294,7 +311,9 @@ SILC_FSM_STATE(silc_client_st_connect) } /* 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); @@ -517,6 +536,8 @@ SILC_FSM_STATE(silc_client_st_connected) return SILC_FSM_CONTINUE; } + silc_schedule_task_del_by_context(conn->internal->schedule, conn); + /* Call connection callback */ conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL, conn->callback_context); @@ -531,8 +552,12 @@ SILC_FSM_STATE(silc_client_st_connect_error) SilcClientConnection conn = fsm_context; /* Signal to close connection */ - conn->internal->disconnected = TRUE; - SILC_FSM_SEMA_POST(&conn->internal->wait_event); + if (!conn->internal->disconnected) { + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + } + + silc_schedule_task_del_by_context(conn->internal->schedule, conn); return SILC_FSM_FINISH; } diff --git a/lib/silcclient/client_entry.c b/lib/silcclient/client_entry.c index 43687fb0..c68dc475 100644 --- a/lib/silcclient/client_entry.c +++ b/lib/silcclient/client_entry.c @@ -750,6 +750,8 @@ SilcClientEntry silc_client_add_client(SilcClient client, silc_mutex_unlock(conn->internal->lock); silc_client_ref_client(client, conn, client_entry); + SILC_LOG_DEBUG(("Added")); + return client_entry; } @@ -1326,6 +1328,8 @@ SilcChannelEntry silc_client_add_channel(SilcClient client, silc_mutex_unlock(conn->internal->lock); silc_client_ref_channel(client, conn, channel); + SILC_LOG_DEBUG(("Added")); + return channel; } @@ -1694,6 +1698,8 @@ SilcServerEntry silc_client_add_server(SilcClient client, silc_mutex_unlock(conn->internal->lock); silc_client_ref_server(client, conn, server_entry); + SILC_LOG_DEBUG(("Added")); + return server_entry; } diff --git a/lib/silcclient/client_internal.h b/lib/silcclient/client_internal.h index 3dbcd672..8f56f357 100644 --- a/lib/silcclient/client_internal.h +++ b/lib/silcclient/client_internal.h @@ -28,6 +28,7 @@ #include "client_prvmsg.h" #include "client_channel.h" #include "client_notify.h" +#include "client_keyagr.h" /****************************** Definitions *********************************/ @@ -41,6 +42,13 @@ /********************************** Types ***********************************/ +/* Public key verification context */ +typedef struct { + SilcSKE ske; + SilcSKEVerifyCbCompletion completion; + void *completion_context; +} *VerifyKeyContext; + /* Context to hold the connection authentication request callbacks that will be called when the server has replied back to our request about current authentication method in the session. */ @@ -214,9 +222,6 @@ void silc_client_command_free(SilcClientCommandContext cmd); void silc_client_ftp(SilcClient client, SilcClientConnection conn, SilcPacket packet); -void silc_client_key_agreement(SilcClient client, - SilcClientConnection conn, - SilcPacket packet); void silc_client_connection_auth_request(SilcClient client, SilcClientConnection conn, SilcPacket packet); diff --git a/lib/silcclient/client_keyagr.c b/lib/silcclient/client_keyagr.c index a68d94a0..52dca47c 100644 --- a/lib/silcclient/client_keyagr.c +++ b/lib/silcclient/client_keyagr.c @@ -23,255 +23,320 @@ /************************** Types and definitions ***************************/ -/* Key agreement context */ +/* Key agreement context, used by responder */ struct SilcClientKeyAgreementStruct { SilcClient client; /* Client */ SilcClientConnection conn; /* Server connection */ SilcKeyAgreementCallback completion; /* Key agreement completion */ void *context; /* User context */ - - /* Responder */ - SilcNetListener listener; /* TCP listener */ - SilcStream stream; /* Remote connection (TCP or UDP) */ - - /* Initiator */ - SilcClientConnection client_conn; /* Connection to remote client */ + 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 */ }; /************************ Static utility functions **************************/ -/* TCP network listener callback. Accepts new key agreement connection */ +/* Destroyes key agreement session */ -static void silc_client_tcp_accept(SilcNetStatus status, - SilcStream stream, - void *context) +static void silc_client_keyagr_free(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry) { - SilcClientEntry client_entry = context; - SilcClientKeyAgreement ke = client_entry->ke; + SilcClientKeyAgreement ke = client_entry->internal.ke; + + 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); - ke->stream = stream; - silc_client_process_key_agreement(ke->client, ke->conn, ke); + client_entry->internal.ke = NULL; + client_entry->internal.prv_resp = FALSE; + silc_client_unref_client(client, conn, client_entry); + + silc_free(ke); } -/* UDP network callback. All UDP packets are read from here. */ +/* Key agreement timeout callback */ -static void silc_client_udp_accept(SilcStream stream, - SilcStreamStatus status, - void *context) +SILC_TASK_CALLBACK(silc_client_keyagr_timeout) { + SilcClientEntry client_entry = context; + SilcClientKeyAgreement ke = client_entry->internal.ke; -} + SILC_LOG_DEBUG(("Key agreement %p timeout", ke)); + ke->completion(ke->client, ke->conn, client_entry, + SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context); + + silc_client_keyagr_free(ke->client, ke->conn, client_entry); +} -/* Packet sending function used by the SKE in the key agreement process. */ +/* Client resolving callback. Continues with the key agreement processing */ -static void silc_client_key_agreement_send_packet(SilcSKE ske, - SilcBuffer packet, - SilcPacketType type, - void *context) +static void silc_client_keyagr_resolved(SilcClient client, + SilcClientConnection conn, + SilcStatus status, + SilcDList clients, + void *context) { - SilcProtocol protocol = (SilcProtocol)context; - SilcClientKEInternalContext *ctx = - (SilcClientKEInternalContext *)protocol->context; - void *tmp; - - /* Send the packet immediately. We will assure that the packet is not - encrypted by setting the socket's user_data pointer to NULL. The - silc_client_packet_send would take the keys (wrong keys that is, - because user_data is the current SilcClientConnection) from it and - we cannot allow that. The packets are never encrypted when doing SKE - with another client. */ - tmp = ske->sock->user_data; - ske->sock->user_data = NULL; - silc_client_packet_send(ctx->client, ske->sock, type, NULL, 0, NULL, NULL, - packet->data, packet->len, TRUE); - ske->sock->user_data = tmp; + /* If no client found, ignore the packet, a silent error */ + if (!clients) + silc_fsm_next(context, silc_client_key_agreement_error); + + /* Continue processing the packet */ + SILC_FSM_CALL_CONTINUE(context); } -/* Timeout callback that is called to close the connection and free the - socket connection data. */ +/* Called after application has verified remote host's public key. Responder + function. */ -SILC_TASK_CALLBACK(silc_client_key_agreement_close) +static void silc_client_keyagr_verify_key_cb(SilcBool success, void *context) { - SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context; + VerifyKeyContext verify = context; - silc_schedule_unset_listen_fd(ke->client->schedule, ke->sock->sock); - silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd); - silc_net_close_connection(ke->sock->sock); - silc_net_close_connection(ke->fd); - silc_socket_free(ke->sock); - silc_free(ke); + /* 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); } -/* This callback is called after the key agreement protocol has been - performed. This calls the final completion callback for the application. */ +/* Verify remote host's public key. Responder function. */ -SILC_TASK_CALLBACK(silc_client_key_agreement_final) +static void silc_client_keyagr_verify_key(SilcSKE ske, + SilcPublicKey public_key, + void *context, + SilcSKEVerifyCbCompletion completion, + void *completion_context) { - SilcProtocol protocol = (SilcProtocol)context; - SilcClientKEInternalContext *ctx = - (SilcClientKEInternalContext *)protocol->context; - SilcClient client = (SilcClient)ctx->client; - SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context; - - SILC_LOG_DEBUG(("Start")); - - if (protocol->state == SILC_PROTOCOL_STATE_ERROR || - protocol->state == SILC_PROTOCOL_STATE_FAILURE) { - /* Error occured during protocol */ - ke->client_entry->ke = NULL; - ke->completion(ke->client, ke->conn, ke->client_entry, - SILC_KEY_AGREEMENT_ERROR, NULL, ke->context); - silc_ske_free_key_material(ctx->keymat); - goto out; + 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; } - /* Pass the negotiated key material to the application. The application - is responsible of freeing the key material. */ - ke->client_entry->ke = NULL; - ke->completion(ke->client, ke->conn, ke->client_entry, - SILC_KEY_AGREEMENT_OK, ctx->keymat, ke->context); - - out: - silc_protocol_free(protocol); - if (ctx->ske) - silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); - silc_schedule_task_del_by_fd(client->schedule, ke->fd); - silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd); - silc_net_close_connection(ke->fd); - if (ke->timeout) - silc_schedule_task_del(client->schedule, ke->timeout); - silc_client_del_socket(ke->client, ke->sock); - - silc_schedule_task_add(client->schedule, 0, - silc_client_key_agreement_close, - (void *)ke, 0, 1, - SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - - silc_free(ctx); + 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 agreement callback that is called when remote end has initiated - the key agreement protocol. This accepts the incoming TCP/IP connection - for the key agreement protocol. */ +/* Key exchange protocol completion callback. Responder function. */ -SILC_TASK_CALLBACK(silc_client_process_key_agreement) +static void silc_client_keyagr_completion(SilcSKE ske, + SilcSKEStatus status, + SilcSKESecurityProperties prop, + SilcSKEKeyMaterial keymat, + SilcSKERekeyMaterial rekey, + void *context) { - SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context; - SilcClient client = ke->client; + SilcClientEntry client_entry = context; + SilcClientKeyAgreement ke = client_entry->internal.ke; SilcClientConnection conn = ke->conn; - SilcSocketConnection newsocket; - SilcClientKEInternalContext *proto_ctx; - int sock; - - SILC_LOG_DEBUG(("Start")); - - sock = silc_net_accept_connection(ke->fd); - if (sock < 0) { - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, - "Could not accept key agreement connection: ", - strerror(errno)); - ke->client_entry->ke = NULL; - ke->completion(ke->client, ke->conn, ke->client_entry, - SILC_KEY_AGREEMENT_ERROR, NULL, ke->context); - silc_schedule_task_del_by_fd(client->schedule, ke->fd); - silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd); - silc_net_close_connection(ke->fd); - if (ke->timeout) - silc_schedule_task_del(client->schedule, ke->timeout); - silc_free(ke); + 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; + + 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 socket options */ - silc_net_set_socket_nonblock(sock); - silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); - - /* Create socket for this connection (it is of type UNKNOWN since this - really is not a real SILC connection. It is only for the key - agreement protocol). */ - silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &newsocket); - ke->sock = newsocket; - - /* Perform name and address lookups for the remote host. */ - silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip); - if (!newsocket->hostname && !newsocket->ip) { - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, - "Could not resolve the remote IP or hostname"); - ke->client_entry->ke = NULL; - ke->completion(ke->client, ke->conn, ke->client_entry, - SILC_KEY_AGREEMENT_ERROR, NULL, ke->context); - silc_schedule_task_del_by_fd(client->schedule, ke->fd); - silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd); - silc_net_close_connection(ke->fd); - if (ke->timeout) - silc_schedule_task_del(client->schedule, ke->timeout); - silc_free(ke); + /* 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; } - if (!newsocket->hostname) - newsocket->hostname = strdup(newsocket->ip); - newsocket->port = silc_net_get_remote_port(sock); - silc_client_add_socket(client, newsocket); - - /* Allocate internal context for key exchange protocol. This is - sent as context for the protocol. */ - proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); - proto_ctx->client = client; - proto_ctx->sock = silc_socket_dup(newsocket); - proto_ctx->rng = client->rng; - proto_ctx->responder = TRUE; - proto_ctx->context = context; - proto_ctx->send_packet = silc_client_key_agreement_send_packet; - proto_ctx->verify = silc_client_protocol_ke_verify_key; - ke->proto_ctx = proto_ctx; - - /* Prepare the connection for key exchange protocol. We allocate the - protocol but will not start it yet. The connector will be the - initiator of the protocol thus we will wait for initiation from - there before we start the protocol. */ - silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, - &newsocket->protocol, proto_ctx, - silc_client_key_agreement_final); - - /* Register the connection for network input and output. This sets - that scheduler will listen for incoming packets for this connection - and sets that outgoing packets may be sent to this connection as well. - However, this doesn't set the scheduler for outgoing traffic, it - will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT, - later when outgoing data is available. */ - context = (void *)client; - SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock); + + /* Process session */ + silc_client_process_key_agreement(ke->client, ke->conn, client_entry); } -/* Timeout occured during key agreement. This means that the key agreement - protocol was not completed in the specified timeout. We will call the - completion callback. */ +/* UDP network listener callback. Accepts new key agreement session. + Responder function. */ -SILC_TASK_CALLBACK(silc_client_key_agreement_timeout) +static SilcBool silc_client_udp_accept(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacket packet, + void *callback_context, + void *stream_context) { - SilcClientKeyAgreement ke = (SilcClientKeyAgreement)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; + } - ke->client_entry->ke = NULL; - ke->completion(ke->client, ke->conn, ke->client_entry, - SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context); + /* 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; +} - if (ke->sock) { - silc_client_del_socket(ke->client, ke->sock); - silc_socket_free(ke->sock); +/* Client connect completion callback. Initiator function. */ + +static void silc_client_keyagr_perform_cb(SilcClient client, + SilcClientConnection conn, + SilcClientConnectionStatus status, + SilcStatus error, + const char *message, + void *context) +{ + SilcClientEntry client_entry = context; + SilcClientKeyAgreement ke = client_entry->internal.ke; + SilcSKEKeyMaterial keymat; + + ke->op = NULL; + + switch (status) { + case SILC_CLIENT_CONN_SUCCESS: + SILC_LOG_DEBUG(("Key agreement %p successful", ke)); + + keymat = silc_ske_get_key_material(conn->internal->ske); + ke->completion(ke->client, ke->conn, client_entry, SILC_KEY_AGREEMENT_OK, + keymat, ke->context); + break; + + case SILC_CLIENT_CONN_ERROR_TIMEOUT: + SILC_LOG_DEBUG(("Key agreement %p timeout", ke)); + ke->completion(ke->client, ke->conn, client_entry, + SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context); + break; + + default: + SILC_LOG_DEBUG(("Key agreement %p error %d", ke, status)); + ke->completion(ke->client, ke->conn, client_entry, + SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context); + break; } - if (ke->proto_ctx && ke->proto_ctx->ske) - silc_ske_free(ke->proto_ctx->ske); - ke->client_entry->ke = NULL; - if (ke->fd) - silc_schedule_task_del_by_fd(ke->client->schedule, ke->fd); - silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd); - silc_net_close_connection(ke->fd); - silc_free(ke); + + /* Close the created 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 @@ -281,23 +346,24 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_timeout) void silc_client_send_key_agreement(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, - const char *local_ip, - const char *bind_ip, - int port, - SilcUInt32 timeout_secs, - SilcBool udp, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, SilcKeyAgreementCallback completion, void *context) { SilcClientKeyAgreement ke = NULL; - SilcAsyncOperation op; SilcBuffer buffer; - SilcUInt16 ports = NULL; + SilcUInt16 port = 0, protocol = 0; + char *local_ip = NULL; + SilcStream stream; + + SILC_LOG_DEBUG(("Sending key agreement")); if (!client_entry) return; - if (client_entry->internal->ke) { + if (client_entry->internal.ke) { completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED, NULL, context); return; @@ -310,7 +376,7 @@ void silc_client_send_key_agreement(SilcClient client, } /* If local IP is provided, create listener */ - if (local_ip || bind_ip) { + if (params && (params->local_ip || params->bind_ip)) { ke = silc_calloc(1, sizeof(*ke)); if (!ke) { completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY, @@ -319,187 +385,133 @@ void silc_client_send_key_agreement(SilcClient client, } /* Create network listener */ - if (udp) { + if (params->udp) { /* UDP listener */ - ke->stream = - silc_net_udp_connect(bind_ip ? bind_ip : local_ip, port, NULL, 0, - client_entry); - if (!ke->stream) { + 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", - bind_ip ? bind_ip : local_ip, port, strerror(errno)); + 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_stream_set_notifier(ke->stream, conn->schedule, - silc_client_udp_accept, client_entry); + 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->listener = - silc_net_tcp_create_listener(bind_ip ? &bind_ip : - &local_ip, 1, port, FALSE, - FALSE, conn->internal->schedule, + 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->listener) { + if (!ke->tcp_listener) { client->internal->ops->say( client, conn, SILC_CLIENT_MESSAGE_ERROR, "Cannot create listener on %s on port %d: %s", - bind_ip ? bind_ip : local_ip, port, strerror(errno)); + 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); + } } + local_ip = params->local_ip; + protocol = params->udp; + ke->client = client; 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; } - /* Add key agreement timeout task */ - silc_schedule_task_add_timeout(conn->internal->schedule, - silc_client_key_agreement_timeout, - client_entry, timeout_secs, 0); - /* Encode the key agreement payload */ - if (ke && ke->listener) - ports = silc_net_listener_get_port(ke->listener, NULL); - buffer = silc_key_agreement_payload_encode(local_ip, (port ? port : - ports ? ports[0] : 0)); + buffer = silc_key_agreement_payload_encode(local_ip, protocol, port); if (!buffer) { - if (ke) { - if (ke->listener) - silc_net_close_listener(ke->listener); - silc_free(ke); - } completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY, NULL, context); + silc_client_keyagr_free(client, conn, client_entry); return; } - silc_free(ports); - - if (ke) { - silc_client_ref_client(client, conn, client_entry); - client_entry->internal.ke = ke; - } /* Send the key agreement packet to the client */ - silc_packet_send(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0, - silc_buffer_data(buffer), silc_buffer_len(data)); - - silc_buffer_free(buffer); -} - -/* Callback that is called after connection has been created. This actually - starts the key agreement protocol. This is initiator function. */ - -SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start) -{ - SilcClientInternalConnectContext *ctx = - (SilcClientInternalConnectContext *)context; - SilcClient client = ctx->client; - SilcClientConnection conn = ctx->conn; - SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context; - int opt, opt_len = sizeof(opt); - - SILC_LOG_DEBUG(("Start")); - - /* Check the socket status as it might be in error */ - silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len); - if (opt != 0) { - if (ctx->tries < 2) { - /* Connection failed but lets try again */ - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Could not connect to client %s: %s", - ctx->host, strerror(opt)); - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, - "Connecting to port %d of client %s resumed", - ctx->port, ctx->host); - - /* Unregister old connection try */ - silc_schedule_unset_listen_fd(client->schedule, fd); - silc_net_close_connection(fd); - silc_schedule_task_del(client->schedule, ctx->task); - - /* Try again */ - silc_client_connect_to_client_internal(ctx); - ctx->tries++; - } else { - /* Connection failed and we won't try anymore */ - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Could not connect to client %s: %s", - ctx->host, strerror(opt)); - silc_schedule_unset_listen_fd(client->schedule, fd); - silc_net_close_connection(fd); - silc_schedule_task_del(client->schedule, ctx->task); - silc_free(ctx->host); - silc_free(ctx); - - /* Call the completion callback */ - ke->completion(ke->client, ke->conn, ke->client_entry, - SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context); - silc_free(ke); - } + if (!silc_packet_send_ext(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0, + 0, NULL, SILC_ID_CLIENT, &client_entry->id, + silc_buffer_datalen(buffer), NULL, NULL)) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR, + NULL, context); + silc_client_keyagr_free(client, conn, client_entry); + silc_buffer_free(buffer); return; } - silc_schedule_unset_listen_fd(client->schedule, fd); - silc_schedule_task_del(client->schedule, ctx->task); - - ke->fd = fd; + /* Add key agreement timeout task */ + if (params && params->timeout_secs) + silc_schedule_task_add_timeout(conn->internal->schedule, + silc_client_keyagr_timeout, + client_entry, params->timeout_secs, 0); - /* Now actually perform the key agreement protocol */ - silc_client_perform_key_agreement_fd(ke->client, ke->conn, - ke->client_entry, ke->fd, ctx->host, - ke->completion, ke->context); - silc_free(ke); - silc_free(ctx->host); - silc_free(ctx); + silc_buffer_free(buffer); } -/* Performs the actual key agreement protocol. Application may use this - to initiate the key agreement protocol. This can be called for example - after the application has received the `key_agreement' client operation, - and did not return TRUE from it. - - The `hostname' is the remote hostname (or IP address) and the `port' - is the remote port. The `completion' callback with the `context' will - be called after the key agreement protocol. - - NOTE: If the application returns TRUE in the `key_agreement' client - operation the library will automatically start the key agreement. In this - case the application must not call this function. However, application - may choose to just ignore the `key_agreement' client operation (and - merely just print information about it on the screen) and call this - function when the user whishes to do so (by, for example, giving some - specific command). Thus, the API provides both, automatic and manual - initiation of the key agreement. Calling this function is the manual - initiation and returning TRUE in the `key_agreement' client operation - is the automatic initiation. */ +/* Perform key agreement protocol as initiator. Conneects to remote host. */ void silc_client_perform_key_agreement(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, - char *hostname, - int port, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + char *hostname, int port, SilcKeyAgreementCallback completion, void *context) { SilcClientKeyAgreement ke; - SILC_LOG_DEBUG(("Start")); - - if (!client_entry) - return; + SILC_LOG_DEBUG(("Performing key agreement")); - if (!hostname || !port) { - completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE, + if (!client_entry || !hostname || !port) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR, NULL, context); return; } @@ -511,43 +523,55 @@ void silc_client_perform_key_agreement(SilcClient client, } ke = silc_calloc(1, sizeof(*ke)); + if (!ke) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY, + NULL, context); + return; + } ke->client = client; ke->conn = conn; - ke->client_entry = client_entry; ke->completion = completion; ke->context = context; - - /* Connect to the remote client */ - ke->fd = silc_client_connect_to_client(client, conn, port, hostname, ke); - if (ke->fd < 0) { - completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE, + silc_client_ref_client(client, conn, client_entry); + client_entry->internal.ke = ke; + + if (params) + 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)) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR, NULL, context); - silc_free(ke); + silc_client_keyagr_free(client, conn, client_entry); return; } } -/* Same as above but application has created already the connection to - the remote host. The `sock' is the socket to the remote connection. - Application can use this function if it does not want the client library - to create the connection. */ - -void silc_client_perform_key_agreement_fd(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - int sock, - char *hostname, - SilcKeyAgreementCallback completion, - void *context) +/* Same as above but caller has created connection. */ + +void +silc_client_perform_key_agreement_stream(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + SilcStream stream, + SilcKeyAgreementCallback completion, + void *context) { SilcClientKeyAgreement ke; - SilcClientKEInternalContext *proto_ctx; - SilcProtocol protocol; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Performing key agreement")); - if (!client_entry) + if (!client_entry || !stream) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR, + NULL, context); return; + } if (client_entry == conn->local_entry) { completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED, @@ -556,48 +580,31 @@ void silc_client_perform_key_agreement_fd(SilcClient client, } ke = silc_calloc(1, sizeof(*ke)); + if (!ke) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY, + NULL, context); + return; + } ke->client = client; ke->conn = conn; - ke->client_entry = client_entry; - ke->fd = sock; ke->completion = completion; ke->context = context; - - /* Allocate new socket connection object */ - silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock); - silc_client_add_socket(client, ke->sock); - ke->sock->hostname = strdup(hostname); - ke->sock->port = silc_net_get_remote_port(sock); - - /* Allocate internal context for key exchange protocol. This is - sent as context for the protocol. */ - proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); - proto_ctx->client = client; - proto_ctx->sock = silc_socket_dup(ke->sock); - proto_ctx->rng = client->rng; - proto_ctx->responder = FALSE; - proto_ctx->context = ke; - proto_ctx->send_packet = silc_client_key_agreement_send_packet; - proto_ctx->verify = silc_client_protocol_ke_verify_key; - ke->proto_ctx = proto_ctx; - - /* Perform key exchange protocol. */ - silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, - &protocol, (void *)proto_ctx, - silc_client_key_agreement_final); - ke->sock->protocol = protocol; - - /* Register the connection for network input and output. This sets - that scheduler will listen for incoming packets for this connection - and sets that outgoing packets may be sent to this connection as well. - However, this doesn't set the scheduler for outgoing traffic, it will - be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT, - later when outgoing data is available. */ - context = (void *)client; - SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock); - - /* Execute the protocol */ - silc_protocol_execute(protocol, client->schedule, 0, 0); + silc_client_ref_client(client, conn, client_entry); + client_entry->internal.ke = ke; + + if (params) + 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)) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR, + NULL, context); + silc_client_keyagr_free(client, conn, client_entry); + return; + } } /* This function can be called to unbind the hostname and the port for @@ -611,98 +618,91 @@ void silc_client_abort_key_agreement(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { - if (!client_entry) + SilcClientKeyAgreement ke; + + if (!client_entry || !client_entry->internal.ke) return; - if (client_entry->ke) { - SilcClientKeyAgreement ke; + ke = client_entry->internal.ke; - if (client_entry->ke->sock) { - silc_client_del_socket(client_entry->ke->client, client_entry->ke->sock); - silc_socket_free(client_entry->ke->sock); - } - silc_schedule_task_del_by_fd(client->schedule, client_entry->ke->fd); - if (client_entry->ke->timeout) - silc_schedule_task_del(client->schedule, - client_entry->ke->timeout); - ke = client_entry->ke; - client_entry->ke = NULL; - ke->completion(client, conn, client_entry, - SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context); - silc_free(ke); - } + SILC_LOG_DEBUG(("Abort key agreement %p")); + + ke->completion(client, conn, client_entry, + SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context); + + silc_client_keyagr_free(client, conn, client_entry); } -/* Callback function that is called after we've resolved the client - information who sent us the key agreement packet from the server. - We actually call the key_agreement client operation now. */ +/* Key agreement packet received */ -static void -silc_client_key_agreement_resolve_cb(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) +SILC_FSM_STATE(silc_client_key_agreement) { - SilcPacketContext *packet = (SilcPacketContext *)context; + SilcClientConnection conn = fsm_context; + SilcClient client = conn->client; + SilcPacket packet = state_context; + SilcClientID remote_id; + SilcClientEntry remote_client; SilcKeyAgreementPayload payload; - int ret; - SilcKeyAgreementCallback completion; - void *completion_context; - if (!clients) - goto out; + if (packet->src_id_type != SILC_ID_CLIENT) { + /** Invalid packet */ + silc_fsm_next(fsm, silc_client_key_agreement_error); + return SILC_FSM_CONTINUE; + } + + if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT, + &remote_id, sizeof(remote_id))) { + /** Invalid source ID */ + silc_fsm_next(fsm, silc_client_key_agreement_error); + return SILC_FSM_CONTINUE; + } + + /* 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]) { + /** Resolve client info */ + silc_client_unref_client(client, conn, remote_client); + SILC_FSM_CALL(silc_client_get_client_by_id_resolve( + client, conn, &remote_id, NULL, + silc_client_keyagr_resolved, fsm)); + /* NOT REACHED */ + } /* Parse the key agreement payload */ - payload = silc_key_agreement_payload_parse(packet->buffer->data, - packet->buffer->len); - if (!payload) - goto out; - - /* Call the key_agreement client operation */ - ret = client->internal->ops->key_agreement( - client, conn, clients[0], - silc_key_agreement_get_hostname(payload), - silc_key_agreement_get_port(payload), - &completion, &completion_context); + payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer), + silc_buffer_len(&packet->buffer)); + if (!payload) { + /** Malformed Payload */ + SILC_LOG_DEBUG(("Malformed key agreement payload")); + silc_fsm_next(fsm, silc_client_key_agreement_error); + return SILC_FSM_CONTINUE; + } + + /* If remote did not provide connection endpoint, we will assume that we + will provide it and will be responder. */ + if (!silc_key_agreement_get_hostname(payload)) + remote_client->internal.prv_resp = TRUE; + else + remote_client->internal.prv_resp = FALSE; - /* If the user returned TRUE then we'll start the key agreement right - here and right now. */ - if (ret == TRUE) - silc_client_perform_key_agreement(client, conn, clients[0], - silc_key_agreement_get_hostname(payload), - silc_key_agreement_get_port(payload), - completion, completion_context); + /* Notify application for key agreement request */ + client->internal->ops->key_agreement( + client, conn, remote_client, + silc_key_agreement_get_hostname(payload), + silc_key_agreement_get_protocol(payload), + silc_key_agreement_get_port(payload)); silc_key_agreement_payload_free(payload); - out: - silc_packet_context_free(packet); + silc_packet_free(packet); + return SILC_FSM_FINISH; } -/* Received Key Agreement packet from remote client. Process the packet - and resolve the client information from the server before actually - letting the application know that we've received this packet. Then - call the key_agreement client operation and let the user decide - whether we perform the key agreement protocol now or not. */ +/* Key agreement packet processing error */ -void silc_client_key_agreement(SilcClient client, - SilcSocketConnection sock, - SilcPacketContext *packet) +SILC_FSM_STATE(silc_client_key_agreement_error) { - SilcClientID *remote_id; - - if (packet->src_id_type != SILC_ID_CLIENT) - return; - - remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, - SILC_ID_CLIENT); - if (!remote_id) - return; - - silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id, - NULL, - silc_client_key_agreement_resolve_cb, - silc_packet_context_dup(packet)); - silc_free(remote_id); + SilcPacket packet = state_context; + silc_packet_free(packet); + return SILC_FSM_FINISH; } diff --git a/lib/silcclient/client_keyagr.h b/lib/silcclient/client_keyagr.h new file mode 100644 index 00000000..f386a575 --- /dev/null +++ b/lib/silcclient/client_keyagr.h @@ -0,0 +1,26 @@ +/* + + client_keyagr.h + + Author: Pekka Riikonen + + Copyright (C) 2006 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef CLIENT_KEYAGR_H +#define CLIENT_KEYAGR_H + +SILC_FSM_STATE(silc_client_key_agreement); +SILC_FSM_STATE(silc_client_key_agreement_error); + +#endif /* CLIENT_KEYAGR_H */ diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index e498e9c6..fd8deeb0 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -495,17 +495,9 @@ SILC_FSM_STATE(silc_client_notify_signoff) /* Notify application */ NOTIFY(client, conn, type, client_entry, tmp); - /* Remove from all channels */ - silc_client_remove_from_channels(client, conn, client_entry); - -#if 0 - /* Remove from cache */ - silc_idcache_del_by_context(conn->internal->client_cache, client_entry); -#endif - - /* Free data */ + /* Delete client */ + silc_client_del_client(client, conn, client_entry); silc_client_unref_client(client, conn, client_entry); - silc_client_del_client_entry(client, conn, client_entry); out: /** Notify processed */ diff --git a/lib/silcclient/client_prvmsg.c b/lib/silcclient/client_prvmsg.c index a4addb35..25ee5650 100644 --- a/lib/silcclient/client_prvmsg.c +++ b/lib/silcclient/client_prvmsg.c @@ -345,6 +345,33 @@ SilcBool silc_client_private_message_wait(SilcClientConnection conn, /*************************** Private Message Key ****************************/ +/* Sends private message key request. Sender of this packet is initiator + when setting the private message key. */ + +static SilcBool +silc_client_send_private_message_key_request(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry) +{ + const char *cipher, *hmac; + + SILC_LOG_DEBUG(("Sending private message key request")); + + cipher = silc_cipher_get_name(client_entry->internal.send_key); + hmac = silc_hmac_get_name(client_entry->internal.hmac_send); + + /* Send the packet */ + return silc_packet_send_va_ext(conn->stream, + SILC_PACKET_PRIVATE_MESSAGE_KEY, + 0, 0, NULL, SILC_ID_CLIENT, + &client_entry->id, NULL, NULL, + SILC_STR_UI_SHORT(strlen(cipher)), + SILC_STR_DATA(cipher, strlen(cipher)), + SILC_STR_UI_SHORT(strlen(hmac)), + SILC_STR_DATA(hmac, strlen(hmac)), + SILC_STR_END); +} + /* Client resolving callback. Here we simply mark that we are the responder side of this private message key request. */ @@ -377,7 +404,9 @@ static void silc_client_private_message_key_cb(SilcClient client, client_entry->internal.prv_resp = TRUE; /* XXX we should notify application that remote wants to set up the - static key */ + static key. And we should tell if we already have key with remote. + Application should return status telling whether to delete the key + or not. */ out: silc_free(cipher); @@ -414,26 +443,9 @@ SILC_FSM_STATE(silc_client_private_message_key) fsm)); } -/* Adds private message key to the client library. The key will be used to - encrypt all private message between the client and the remote client - indicated by the `client_entry'. If the `key' is NULL and the boolean - value `generate_key' is TRUE the library will generate random key. - The `key' maybe for example pre-shared-key, passphrase or similar. - The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure - that the requirements of the SILC protocol are met. The API, however, - allows to allocate any cipher and HMAC. - - If `responder' is TRUE then the sending and receiving keys will be - set according the client being the receiver of the private key. If - FALSE the client is being the sender (or negotiator) of the private - key. - - It is not necessary to set key for normal private message usage. If the - key is not set then the private messages are encrypted using normal - session keys. Setting the private key, however, increases the security. - - Returns FALSE if the key is already set for the `client_entry', TRUE - otherwise. */ +/* Adds new private message key to `client_entry'. If we are setting this + before receiving request for it from `client_entry' we will send the + request to the client. Otherwise, we are responder side. */ SilcBool silc_client_add_private_message_key(SilcClient client, SilcClientConnection conn, @@ -441,15 +453,10 @@ SilcBool silc_client_add_private_message_key(SilcClient client, const char *cipher, const char *hmac, unsigned char *key, - SilcUInt32 key_len, - SilcBool generate_key, - SilcBool responder) + SilcUInt32 key_len) { - unsigned char private_key[32]; - SilcUInt32 len; SilcSKEKeyMaterial keymat; SilcBool ret; - int i; if (!client || !client_entry) return FALSE; @@ -469,15 +476,6 @@ SilcBool silc_client_add_private_message_key(SilcClient client, if (!silc_hmac_is_supported(hmac)) return FALSE; - /* Generate key if not provided */ - if (generate_key == TRUE) { - len = 32; - for (i = 0; i < len; i++) - private_key[i] = silc_rng_get_byte_fast(client->rng); - key = private_key; - key_len = len; - } - /* Save the key */ client_entry->internal.key = silc_memdup(key, key_len); client_entry->internal.key_len = key_len; @@ -490,31 +488,29 @@ SilcBool silc_client_add_private_message_key(SilcClient client, /* Set the key into use */ ret = silc_client_add_private_message_key_ske(client, conn, client_entry, - cipher, hmac, keymat, - responder); - - if (!generate_key) - client_entry->internal.generated = FALSE; + cipher, hmac, keymat); + client_entry->internal.generated = FALSE; /* Free the key material */ silc_ske_free_key_material(keymat); + /* If we are setting the key without a request from the remote client, + we will send request to remote. */ + if (!client_entry->internal.prv_resp) + silc_client_send_private_message_key_request(client, conn, client_entry); + return ret; } /* Same as above but takes the key material from the SKE key material - structure. This structure is received if the application uses the - silc_client_send_key_agreement to negotiate the key material. The - `cipher' and `hmac' SHOULD be provided as it is negotiated also in - the SKE protocol. */ + structure. */ SilcBool silc_client_add_private_message_key_ske(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, const char *cipher, const char *hmac, - SilcSKEKeyMaterial keymat, - SilcBool responder) + SilcSKEKeyMaterial keymat) { if (!client || !client_entry) return FALSE; @@ -547,7 +543,7 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client, return FALSE; /* Set the keys */ - if (responder == TRUE) { + if (client_entry->internal.prv_resp) { silc_cipher_set_key(client_entry->internal.send_key, keymat->receive_enc_key, keymat->enc_key_len); @@ -584,40 +580,6 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client, return TRUE; } -/* Sends private message key indicator. The sender of this packet is - going to be the initiator, if and when, the users set up a static - private message key (not Key Agreement). */ - -SilcBool -silc_client_send_private_message_key_request(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry) -{ - const char *cipher, *hmac; - - if (!client || !conn || !client_entry) - return FALSE; - - if (!client_entry->internal.send_key && !client_entry->internal.receive_key) - return FALSE; - - SILC_LOG_DEBUG(("Sending private message key indicator")); - - cipher = silc_cipher_get_name(client_entry->internal.send_key); - hmac = silc_hmac_get_name(client_entry->internal.hmac_send); - - /* Send the packet */ - return silc_packet_send_va_ext(conn->stream, - SILC_PACKET_PRIVATE_MESSAGE_KEY, - 0, 0, NULL, SILC_ID_CLIENT, - &client_entry->id, NULL, NULL, - SILC_STR_UI_SHORT(strlen(cipher)), - SILC_STR_DATA(cipher, strlen(cipher)), - SILC_STR_UI_SHORT(strlen(hmac)), - SILC_STR_DATA(hmac, strlen(hmac)), - SILC_STR_END); -} - /* Removes the private message from the library. The key won't be used after this to protect the private messages with the remote `client_entry' client. Returns FALSE on error, TRUE otherwise. */ @@ -643,6 +605,7 @@ SilcBool silc_client_del_private_message_key(SilcClient client, client_entry->internal.send_key = NULL; client_entry->internal.receive_key = NULL; client_entry->internal.key = NULL; + client_entry->internal.prv_resp = FALSE; return TRUE; } diff --git a/lib/silcclient/client_register.c b/lib/silcclient/client_register.c index 84f35f72..b0ddc907 100644 --- a/lib/silcclient/client_register.c +++ b/lib/silcclient/client_register.c @@ -213,6 +213,8 @@ SILC_FSM_STATE(silc_client_st_register_complete) conn->callback_context); conn->internal->registering = FALSE; + silc_schedule_task_del_by_context(conn->internal->schedule, conn); + return SILC_FSM_FINISH; } @@ -226,13 +228,17 @@ SILC_FSM_STATE(silc_client_st_register_error) SILC_LOG_DEBUG(("Error registering to network")); /* Signal to close connection */ - conn->internal->disconnected = TRUE; - SILC_FSM_SEMA_POST(&conn->internal->wait_event); + if (!conn->internal->disconnected) { + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + } /* Call connect callback */ conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL, conn->callback_context); + silc_schedule_task_del_by_context(conn->internal->schedule, conn); + return SILC_FSM_FINISH; } diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 468ac1bb..b1d233fc 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -1079,8 +1079,10 @@ SILC_FSM_STATE(silc_client_command_quit_final) 0, NULL, conn->callback_context); /* Signal to close connection */ - conn->internal->disconnected = TRUE; - SILC_FSM_SEMA_POST(&conn->internal->wait_event); + if (!conn->internal->disconnected) { + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + } return SILC_FSM_FINISH; } diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index f5adc9c2..bbb4f2a2 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -25,13 +25,13 @@ /************************** Types and definitions ***************************/ /* Calls error command reply callback back to command sender. */ -#define ERROR_CALLBACK(error) \ +#define ERROR_CALLBACK(err) \ do { \ void *arg1 = NULL, *arg2 = NULL; \ if (cmd->status != SILC_STATUS_OK) \ silc_status_get_args(cmd->status, args, &arg1, &arg2); \ else \ - cmd->status = error; \ + cmd->status = cmd->error = err; \ SILC_LOG_DEBUG(("Error in command reply: %s", \ silc_get_status_message(cmd->status))); \ silc_client_command_callback(cmd, arg1, arg2); \ @@ -424,7 +424,7 @@ SILC_FSM_STATE(silc_client_command_reply_whois) silc_client_add_client(client, conn, nickname, username, realname, &id.u.client_id, mode); if (!client_entry) { - ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT); goto out; } silc_client_ref_client(client, conn, client_entry); @@ -560,7 +560,7 @@ SILC_FSM_STATE(silc_client_command_reply_identify) silc_client_add_client(client, conn, name, info, NULL, &id.u.client_id, 0); if (!client_entry) { - ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT); goto out; } silc_client_ref_client(client, conn, client_entry); diff --git a/lib/silcclient/silcclient.h b/lib/silcclient/silcclient.h index b44b09ff..2426cb57 100644 --- a/lib/silcclient/silcclient.h +++ b/lib/silcclient/silcclient.h @@ -299,7 +299,7 @@ typedef void (*SilcKeyAgreementCallback)(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, SilcKeyAgreementStatus status, - SilcSKEKeyMaterial *key, + SilcSKEKeyMaterial key, void *context); /****s* silcclient/SilcClientAPI/SilcPrivateMessageKeys @@ -320,8 +320,7 @@ typedef struct { SilcClientEntry client_entry; /* The remote client entry */ char *cipher; /* The cipher name */ unsigned char *key; /* The original key, If the appliation - provided it. This is NULL if the - library generated the key or if + provided it. This is NULL if the SKE key material was used. */ SilcUInt32 key_len; /* The key length */ } *SilcPrivateMessageKeys; @@ -554,18 +553,17 @@ typedef struct { void (*ask_passphrase)(SilcClient client, SilcClientConnection conn, SilcAskPassphrase completion, void *context); - /* Asks whether the user would like to perform the key agreement protocol. - This is called after we have received an key agreement packet or an - reply to our key agreement packet. This returns TRUE if the user wants - the library to perform the key agreement protocol and FALSE if it is not - desired (application may start it later by calling the function - silc_client_perform_key_agreement). If TRUE is returned also the - `completion' and `context' arguments must be set by the application. */ - SilcBool (*key_agreement)(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, - const char *hostname, SilcUInt16 port, - SilcKeyAgreementCallback *completion, - void **context); + /* Called to indicate that incoming key agreement request has been + received. If the application wants to perform key agreement it may + call silc_client_perform_key_agreement to initiate key agreementn or + silc_client_send_key_agreement to provide connection point to the + remote client in case the `hostname' is NULL. If key agreement is + not desired this request can be ignored. The `protocol' is either + value 0 for TCP or value 1 for UDP. */ + void (*key_agreement)(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, + const char *hostname, SilcUInt16 protocol, + SilcUInt16 port); /* Notifies application that file transfer protocol session is being requested by the remote client indicated by the `client_entry' from @@ -600,6 +598,7 @@ typedef struct { /* Called when the client library is up and running. After this callback is called the application may start using the client library APIs. */ void (*running)(SilcClient client, void *application); + } SilcClientOperations; /***/ @@ -847,7 +846,8 @@ void silc_client_stop(SilcClient client); * * Client connection parameters. This can be filled by the application * and given as argument to silc_client_connect_to_server, - * silc_client_connect_to_client or silc_client_key_exchange. + * silc_client_connect_to_client, silc_client_key_exchange or + * silc_client_send_key_agreement. * * SOURCE */ @@ -884,14 +884,19 @@ typedef struct { void *auth; SilcUInt32 auth_len; - /* If this boolean is set to TRUE then the client's connection to the - remote host will use UDP instead of TCP. The `local_ip' specifies - the local IP address used with the UDP connection, and it must be - non-NULL. If the `local_port' is non-zero it will be used as local - port with the UDP connection. The remote host will also send packets - to the specified address and port. */ + /* If this boolean is set to TRUE then the connection will use UDP instead + of TCP. If UDP is set the also the next `local_ip' and `local_port' + must be set. */ SilcBool udp; + + /* The `local_ip' specifies the local IP address used with the connection. + It must be non-NULL if `udp' boolean is TRUE. If the `local_port' is + non-zero it will be used as local port with UDP connection. The remote + host will also send packets to the specified address and port. If the + `bind_ip' is non-NULL a listener is bound to that address instead of + `local_ip'. */ char *local_ip; + char *bind_ip; int local_port; /* If this boolean is set to TRUE then the key exchange is done with @@ -918,6 +923,9 @@ typedef struct { unsigned char *detach_data; SilcUInt32 detach_data_len; + /* Connection timeout. If non-zero, the connection will timeout unless + the SILC connection is completed in the specified amount of time. */ + SilcUInt32 timeout_secs; } SilcClientConnectionParams; /***/ @@ -1387,29 +1395,26 @@ SilcBool silc_client_command_pending(SilcClientConnection conn, * const char *cipher, * const char *hmac, * unsigned char *key, - * SilcUInt32 key_len, - * SilcBool generate_key, - * SilcBool responder); + * SilcUInt32 key_len); * * DESCRIPTION * - * Adds private message key to the client library. The key will be used to - * encrypt all private message between the client and the remote client - * indicated by the `client_entry'. If the `key' is NULL and the boolean - * value `generate_key' is TRUE the library will generate random key. - * The `key' maybe for example pre-shared-key, passphrase or similar. - * The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure - * that the requirements of the SILC protocol are met. The API, however, - * allows to allocate any cipher and HMAC. + * Adds a static private message key to the client library. The key + * will be used to encrypt all private message between the client and + * the remote client indicated by the `client_entry'. The `key' can + * be for example a pre-shared-key, passphrase or similar shared secret + * string. The `cipher' and `hmac' MAY be provided but SHOULD be NULL + * to assure that the requirements of the SILC protocol are met. The + * API, however, allows to allocate any cipher and HMAC. * - * If `responder' is TRUE then the sending and receiving keys will be - * set according the client being the receiver of the private key. If - * FALSE the client is being the sender (or negotiator) of the private - * key. + * If the private message key is added to client without first receiving + * a request for it from the remote `client_entry' this function will + * send the request to `client_entry'. Note that, the actual key is + * not sent to the network. * * It is not necessary to set key for normal private message usage. If the * key is not set then the private messages are encrypted using normal - * session keys. Setting the private key, however, increases the security. + * session keys. Setting the private key, however, increases the security. * * Returns FALSE if the key is already set for the `client_entry', TRUE * otherwise. @@ -1421,9 +1426,7 @@ SilcBool silc_client_add_private_message_key(SilcClient client, const char *cipher, const char *hmac, unsigned char *key, - SilcUInt32 key_len, - SilcBool generate_key, - SilcBool responder); + SilcUInt32 key_len); /****f* silcclient/SilcClientAPI/silc_client_add_private_message_key_ske * @@ -1440,9 +1443,9 @@ SilcBool silc_client_add_private_message_key(SilcClient client, * DESCRIPTION * * Same as silc_client_add_private_message_key but takes the key material - * from the SKE key material structure. This structure is received if + * from the SKE key material structure. This structure is received if * the application uses the silc_client_send_key_agreement to negotiate - * the key material. The `cipher' and `hmac' SHOULD be provided as it is + * the key material. The `cipher' and `hmac' SHOULD be provided as it is * negotiated also in the SKE protocol. * ***/ @@ -1451,8 +1454,7 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client, SilcClientEntry client_entry, const char *cipher, const char *hmac, - SilcSKEKeyMaterial key, - SilcBool responder); + SilcSKEKeyMaterial key); /****f* silcclient/SilcClientAPI/silc_client_del_private_message_key * @@ -1498,40 +1500,6 @@ silc_client_list_private_message_keys(SilcClient client, SilcClientConnection conn, SilcUInt32 *key_count); -/****f* silcclient/SilcClientAPI/silc_client_send_private_message_key_request - * - * SYNOPSIS - * - * SilcBool - * silc_client_send_private_message_key_request(SilcClient client, - * SilcClientConnection conn, - * SilcClientEntry client_entry); - * - * DESCRIPTION - * - * This function can be used to send an private message key indicator - * request to the remote client indicated by 'client_entry'. This can - * be used when setting a static or pre-shared private message key. - * The sender of this packet is the initiator and must set the 'responder' - * argument in silc_client_add_private_message_key function to FALSE. - * The receiver of this indicator request must set it to TRUE, if the - * receiver decides to set a private message key. By using this - * function applications may automate initiator/responder setting in - * private message key functions, without asking from user which one is - * the initiator and which one is responder. - * - * NOTES - * - * The sender of this packet must set the private message key for - * 'client_entry' before calling this function. The 'responder' - * argument MUST be set to FALSE when setting the key. - * - ***/ -SilcBool -silc_client_send_private_message_key_request(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry); - /****f* silcclient/SilcClientAPI/silc_client_free_private_message_keys * * SYNOPSIS @@ -1698,7 +1666,7 @@ void silc_client_current_channel_private_key(SilcClient client, SilcChannelPrivateKey key); -/* Key Agreement routines (client_keyagr.c) */ +/* Key Agreement routines */ /****f* silcclient/SilcClientAPI/silc_client_send_key_agreement * @@ -1707,62 +1675,46 @@ void silc_client_current_channel_private_key(SilcClient client, * void silc_client_send_key_agreement(SilcClient client, * SilcClientConnection conn, * SilcClientEntry client_entry, - * char *hostname, - * int port, - * SilcUInt32 timeout_secs, + * SilcClientConnectionParams *params, + * SilcPublicKey public_key, + * SilcPrivateKey private_key, * SilcKeyAgreementCallback completion, * void *context); * * DESCRIPTION * * Sends key agreement request to the remote client indicated by the - * `client_entry'. If the caller provides the `hostname' and the `port' - * arguments then the library will bind the client to that hostname and - * that port for the key agreement protocol. It also sends the `hostname' - * and the `port' in the key agreement packet to the remote client. This - * would indicate that the remote client may initiate the key agreement - * protocol to the `hostname' on the `port'. If port is zero then the - * bound port is undefined (the operating system defines it). - * - * If the `hostname' and `port' is not provided then empty key agreement - * packet is sent to the remote client. The remote client may reply with - * the same packet including its hostname and port. If the library receives - * the reply from the remote client the `key_agreement' client operation - * callback will be called to verify whether the user wants to perform the - * key agreement or not. - * - * NOTES - * - * NOTE: If the application provided the `hostname' and the `port' and the - * remote side initiates the key agreement protocol it is not verified - * from the user anymore whether the protocol should be executed or not. - * By setting the `hostname' and `port' the user gives permission to - * perform the protocol (we are responder in this case). - * - * NOTE: If the remote side decides not to initiate the key agreement - * or decides not to reply with the key agreement packet then we cannot - * perform the key agreement at all. If the key agreement protocol is - * performed the `completion' callback with the `context' will be called. - * If remote side decides to ignore the request the `completion' will be - * called after the specified timeout, `timeout_secs'. - * - * NOTE: If the `hostname' and the `port' was not provided the `completion' - * will not be called at all since this does nothing more than sending - * a packet to the remote host. - * - * NOTE: There can be only one active key agreement for one client entry. - * Before setting new one, the old one must be finished (it is finished - * after calling the completion callback) or the function - * silc_client_abort_key_agreement must be called. + * `client_entry'. + * + * If `params' is non-NULL and it has the `local_ip' and `local_port' set + * the caller will provide the connection endpoint for the key agreement + * connection. The `bind_ip' can be used to bind to that IP instead of + * `local_ip'. If the `udp' is set to TRUE the connection will be UDP + * instead of TCP. Caller may also set the `repository', `verify_notfound' + * and `timeout_secs' fields in `params'. Other fields are ignored. + * If `params' is NULL, then the `client_entry' is expected to provide + * the connection endpoint for us. It is recommended the `timeout_secs' + * is specified in case the remote client does not reply anything to + * the request. + * + * The `public_key' and `private_key' is our identity in the key agreement. + * + * In case we do not provide the connection endpoint, we will receive + * the `key_agreement' client operation when the remote send its own + * key agreement request packet. We may then there start the key + * agreement with silc_client_perform_key_agreement. If we provided the + * the connection endpoint, the client operation will not be called. + * + * There can be only one active key agreement for `client_entry'. Old + * key agreement may be aborted by calling silc_client_abort_key_agreement. * ***/ void silc_client_send_key_agreement(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, - const char *hostname, - const char *bindhost, - int port, - SilcUInt32 timeout_secs, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, SilcKeyAgreementCallback completion, void *context); @@ -1774,72 +1726,69 @@ void silc_client_send_key_agreement(SilcClient client, * silc_client_perform_key_agreement(SilcClient client, * SilcClientConnection conn, * SilcClientEntry client_entry, - * char *hostname, - * int port, + * SilcClientConnectionParams *params, + * SilcPublicKey public_key, + * SilcPrivateKey private_key, + * char *hostname, int port, * SilcKeyAgreementCallback completion, * void *context); * * DESCRIPTION * - * Performs the actual key agreement protocol. Application may use this - * to initiate the key agreement protocol. This can be called for example - * after the application has received the `key_agreement' client operation, - * and did not return TRUE from it. + * Performs the key agreement protocol. Application may use this to + * initiate the key agreement protocol. Usually this is called after + * receiving the `key_agreement' client operation. * * The `hostname' is the remote hostname (or IP address) and the `port' - * is the remote port. The `completion' callback with the `context' will + * is the remote port. The `completion' callback with the `context' will * be called after the key agreement protocol. * - * NOTES - * - * NOTE: If the application returns TRUE in the `key_agreement' client - * operation the library will automatically start the key agreement. In this - * case the application must not call this function. However, application - * may choose to just ignore the `key_agreement' client operation (and - * merely just print information about it on the screen) and call this - * function when the user whishes to do so (by, for example, giving some - * specific command). Thus, the API provides both, automatic and manual - * initiation of the key agreement. Calling this function is the manual - * initiation and returning TRUE in the `key_agreement' client operation - * is the automatic initiation. + * The `params' is connection parameters and it may be used to define + * the key agreement connection related parameters. It may be NULL. * ***/ void silc_client_perform_key_agreement(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, - char *hostname, - int port, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + char *hostname, int port, SilcKeyAgreementCallback completion, void *context); -/****f* silcclient/SilcClientAPI/silc_client_perform_key_agreement_fd +/****f* silcclient/SilcClientAPI/silc_client_perform_key_agreement_stream * * SYNOPSIS * * void - * silc_client_perform_key_agreement_fd(SilcClient client, - * SilcClientConnection conn, - * SilcClientEntry client_entry, - * int sock, - * char *hostname, - * SilcKeyAgreementCallback completion, - * void *context); + * silc_client_perform_key_agreement_stream( + * SilcClient client, + * SilcClientConnection conn, + * SilcClientEntry client_entry, + * SilcClientConnectionParams *params, + * SilcPublicKey public_key, + * SilcPrivateKey private_key, + * SilcStream stream, + * SilcKeyAgreementCallback completion, + * void *context); * * DESCRIPTION * - * Same as above but application has created already the connection to - * the remote host. The `sock' is the socket to the remote connection. - * Application can use this function if it does not want the client library - * to create the connection. + * Same as silc_client_perform_key_agreement but the caller has created + * the connection. The `stream' is the created connection. * ***/ -void silc_client_perform_key_agreement_fd(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - int sock, - char *hostname, - SilcKeyAgreementCallback completion, - void *context); +void +silc_client_perform_key_agreement_stream(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + SilcStream stream, + SilcKeyAgreementCallback completion, + void *context); /****f* silcclient/SilcClientAPI/silc_client_abort_key_agreement * diff --git a/lib/silcclient/tests/test_silcclient.c b/lib/silcclient/tests/test_silcclient.c index 839b084b..ef5529a0 100644 --- a/lib/silcclient/tests/test_silcclient.c +++ b/lib/silcclient/tests/test_silcclient.c @@ -343,11 +343,10 @@ silc_ask_passphrase(SilcClient client, SilcClientConnection conn, silc_client_perform_key_agreement). If TRUE is returned also the `completion' and `context' arguments must be set by the application. */ -static bool +static void silc_key_agreement(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, const char *hostname, - SilcUInt16 port, SilcKeyAgreementCallback *completion, - void **context) + SilcUInt16 port) { /* MyBot does not support incoming key agreement protocols, it's too simple for that. */ -- 2.24.0