5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcclient.h"
22 #include "client_internal.h"
24 /************************** Types and definitions ***************************/
26 /* Key agreement context, used by responder */
27 struct SilcClientKeyAgreementStruct {
28 SilcClient client; /* Client */
29 SilcClientConnection conn; /* Server connection */
30 SilcClientListener listener; /* Listener */
31 SilcKeyAgreementCallback completion; /* Key agreement completion */
32 void *context; /* User context */
33 SilcAsyncOperation op; /* Async operation, initiator */
36 /************************ Static utility functions **************************/
38 /* Destroyes key agreement session */
40 static void silc_client_keyagr_free(SilcClient client,
41 SilcClientConnection conn,
42 SilcClientEntry client_entry)
44 SilcClientKeyAgreement ke = client_entry->internal.ke;
46 silc_client_listener_free(ke->listener);
47 silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
49 silc_async_abort(ke->op, NULL, NULL);
50 client_entry->internal.ke = NULL;
51 client_entry->internal.prv_resp = FALSE;
52 silc_client_unref_client(client, conn, client_entry);
56 /* Key agreement timeout callback */
58 SILC_TASK_CALLBACK(silc_client_keyagr_timeout)
60 SilcClientEntry client_entry = context;
61 SilcClientKeyAgreement ke = client_entry->internal.ke;
63 SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
65 ke->completion(ke->client, ke->conn, client_entry,
66 SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
68 silc_client_keyagr_free(ke->client, ke->conn, client_entry);
71 /* Client resolving callback. Continues with the key agreement processing */
73 static void silc_client_keyagr_resolved(SilcClient client,
74 SilcClientConnection conn,
79 /* If no client found, ignore the packet, a silent error */
81 silc_fsm_next(context, silc_client_key_agreement_error);
83 /* Continue processing the packet */
84 SILC_FSM_CALL_CONTINUE(context);
87 /* Key exchange completion callback. Called after connected to remote host
88 and performed key exchange, when we are initiator. As responder, this is
89 called after the remote has connected to us and have performed the key
92 static void silc_client_keyagr_completion(SilcClient client,
93 SilcClientConnection conn,
94 SilcClientConnectionStatus status,
99 SilcClientEntry client_entry = context;
100 SilcClientKeyAgreement ke = client_entry->internal.ke;
101 SilcSKEKeyMaterial keymat;
106 case SILC_CLIENT_CONN_SUCCESS:
107 SILC_LOG_DEBUG(("Key agreement %p successful", ke));
109 keymat = silc_ske_get_key_material(conn->internal->ske);
110 ke->completion(ke->client, ke->conn, client_entry, SILC_KEY_AGREEMENT_OK,
111 keymat, ke->context);
114 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
115 SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
116 ke->completion(ke->client, ke->conn, client_entry,
117 SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
121 SILC_LOG_DEBUG(("Key agreement %p error %d", ke, status));
122 ke->completion(ke->client, ke->conn, client_entry,
123 SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
127 /* Close the connection */
129 silc_client_close_connection(ke->client, conn);
131 silc_client_keyagr_free(ke->client, ke->conn, client_entry);
134 /*************************** Key Agreement API ******************************/
136 /* Sends key agreement packet to remote client. If IP addresses are provided
137 creates also listener for íncoming key agreement connection. Supports
138 both TCP and UDP transports. */
140 void silc_client_send_key_agreement(SilcClient client,
141 SilcClientConnection conn,
142 SilcClientEntry client_entry,
143 SilcClientConnectionParams *params,
144 SilcPublicKey public_key,
145 SilcPrivateKey private_key,
146 SilcKeyAgreementCallback completion,
149 SilcClientKeyAgreement ke = NULL;
151 SilcUInt16 port = 0, protocol = 0;
152 char *local_ip = NULL;
154 SILC_LOG_DEBUG(("Sending key agreement"));
158 if (conn->internal->disconnected)
161 if (client_entry->internal.ke) {
162 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED,
167 if (client_entry == conn->local_entry) {
168 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
173 /* If local IP is provided, create listener. If this is not provided,
174 we'll just send empty key agreement payload */
175 if (params && (params->local_ip || params->bind_ip)) {
176 ke = silc_calloc(1, sizeof(*ke));
178 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
183 /* Create listener */
184 ke->listener = silc_client_listener_add(client, conn->internal->schedule,
185 params, public_key, private_key,
186 silc_client_keyagr_completion,
189 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
194 local_ip = params->local_ip;
195 protocol = params->udp;
199 ke->completion = completion;
200 ke->context = context;
201 silc_client_ref_client(client, conn, client_entry);
202 client_entry->internal.ke = ke;
203 client_entry->internal.prv_resp = TRUE;
206 /* Encode the key agreement payload */
207 buffer = silc_key_agreement_payload_encode(local_ip, protocol, port);
209 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
211 silc_client_keyagr_free(client, conn, client_entry);
215 /* Send the key agreement packet to the client */
216 if (!silc_packet_send_ext(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0,
217 0, NULL, SILC_ID_CLIENT, &client_entry->id,
218 silc_buffer_datalen(buffer), NULL, NULL)) {
219 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
221 silc_client_keyagr_free(client, conn, client_entry);
222 silc_buffer_free(buffer);
226 /* Add key agreement timeout task */
227 if (params && params->timeout_secs)
228 silc_schedule_task_add_timeout(conn->internal->schedule,
229 silc_client_keyagr_timeout,
230 client_entry, params->timeout_secs, 0);
232 silc_buffer_free(buffer);
235 /* Perform key agreement protocol as initiator. Conneects to remote host. */
237 void silc_client_perform_key_agreement(SilcClient client,
238 SilcClientConnection conn,
239 SilcClientEntry client_entry,
240 SilcClientConnectionParams *params,
241 SilcPublicKey public_key,
242 SilcPrivateKey private_key,
243 char *hostname, int port,
244 SilcKeyAgreementCallback completion,
247 SilcClientKeyAgreement ke;
249 SILC_LOG_DEBUG(("Performing key agreement"));
251 if (!client_entry || !hostname || !port) {
252 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
257 if (client_entry == conn->local_entry) {
258 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
263 ke = silc_calloc(1, sizeof(*ke));
265 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
271 ke->completion = completion;
272 ke->context = context;
273 silc_client_ref_client(client, conn, client_entry);
274 client_entry->internal.ke = ke;
277 params->no_authentication = TRUE;
279 /* Connect to the remote client. Performs key exchange automatically. */
280 ke->op = silc_client_connect_to_client(client, params, public_key,
281 private_key, hostname, port,
282 silc_client_keyagr_completion,
285 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
287 silc_client_keyagr_free(client, conn, client_entry);
292 /* Same as above but caller has created connection. */
295 silc_client_perform_key_agreement_stream(SilcClient client,
296 SilcClientConnection conn,
297 SilcClientEntry client_entry,
298 SilcClientConnectionParams *params,
299 SilcPublicKey public_key,
300 SilcPrivateKey private_key,
302 SilcKeyAgreementCallback completion,
305 SilcClientKeyAgreement ke;
307 SILC_LOG_DEBUG(("Performing key agreement"));
309 if (!client_entry || !stream) {
310 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
315 if (client_entry == conn->local_entry) {
316 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
321 ke = silc_calloc(1, sizeof(*ke));
323 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
329 ke->completion = completion;
330 ke->context = context;
331 silc_client_ref_client(client, conn, client_entry);
332 client_entry->internal.ke = ke;
335 params->no_authentication = TRUE;
337 /* Perform key exchange protocol */
338 ke->op = silc_client_key_exchange(client, params, public_key,
339 private_key, stream, SILC_CONN_CLIENT,
340 silc_client_keyagr_completion,
343 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
345 silc_client_keyagr_free(client, conn, client_entry);
350 /* This function can be called to unbind the hostname and the port for
351 the key agreement protocol. However, this function has effect only
352 before the key agreement protocol has been performed. After it has
353 been performed the library will automatically unbind the port. The
354 `client_entry' is the client to which we sent the key agreement
357 void silc_client_abort_key_agreement(SilcClient client,
358 SilcClientConnection conn,
359 SilcClientEntry client_entry)
361 SilcClientKeyAgreement ke;
363 if (!client_entry || !client_entry->internal.ke)
366 ke = client_entry->internal.ke;
368 SILC_LOG_DEBUG(("Abort key agreement %p"));
370 ke->completion(client, conn, client_entry,
371 SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
373 silc_client_keyagr_free(client, conn, client_entry);
376 /* Key agreement packet received */
378 SILC_FSM_STATE(silc_client_key_agreement)
380 SilcClientConnection conn = fsm_context;
381 SilcClient client = conn->client;
382 SilcPacket packet = state_context;
383 SilcClientID remote_id;
384 SilcClientEntry remote_client;
385 SilcKeyAgreementPayload payload;
387 if (packet->src_id_type != SILC_ID_CLIENT) {
388 /** Invalid packet */
389 silc_fsm_next(fsm, silc_client_key_agreement_error);
390 return SILC_FSM_CONTINUE;
393 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
394 &remote_id, sizeof(remote_id))) {
395 /** Invalid source ID */
396 silc_fsm_next(fsm, silc_client_key_agreement_error);
397 return SILC_FSM_CONTINUE;
400 /* Check whether we know this client already */
401 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
402 if (!remote_client || !remote_client->internal.valid) {
403 /** Resolve client info */
404 silc_client_unref_client(client, conn, remote_client);
405 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
406 client, conn, &remote_id, NULL,
407 silc_client_keyagr_resolved, fsm));
411 /* Parse the key agreement payload */
412 payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer),
413 silc_buffer_len(&packet->buffer));
415 /** Malformed Payload */
416 SILC_LOG_DEBUG(("Malformed key agreement payload"));
417 silc_fsm_next(fsm, silc_client_key_agreement_error);
418 return SILC_FSM_CONTINUE;
421 /* If remote did not provide connection endpoint, we will assume that we
422 will provide it and will be responder. */
423 if (!silc_key_agreement_get_hostname(payload))
424 remote_client->internal.prv_resp = TRUE;
426 remote_client->internal.prv_resp = FALSE;
428 /* Notify application for key agreement request */
429 client->internal->ops->key_agreement(
430 client, conn, remote_client,
431 silc_key_agreement_get_hostname(payload),
432 silc_key_agreement_get_protocol(payload),
433 silc_key_agreement_get_port(payload));
435 silc_key_agreement_payload_free(payload);
437 silc_packet_free(packet);
438 return SILC_FSM_FINISH;
441 /* Key agreement packet processing error */
443 SILC_FSM_STATE(silc_client_key_agreement_error)
445 SilcPacket packet = state_context;
446 silc_packet_free(packet);
447 return SILC_FSM_FINISH;