5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2014 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 */
35 /************************ Static utility functions **************************/
37 /* Destroyes key agreement session */
39 static void silc_client_keyagr_free(SilcClient client,
40 SilcClientConnection conn,
41 SilcClientEntry client_entry)
43 SilcClientKeyAgreement ke = client_entry->internal.ke;
45 silc_client_listener_free(ke->listener);
46 silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
47 if (client_entry->internal.op)
48 silc_async_abort(client_entry->internal.op, NULL, NULL);
49 client_entry->internal.op = 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;
66 SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
68 ke->completion(ke->client, ke->conn, client_entry,
69 SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
71 silc_client_keyagr_free(ke->client, ke->conn, client_entry);
74 /* Client resolving callback. Continues with the key agreement processing */
76 static void silc_client_keyagr_resolved(SilcClient client,
77 SilcClientConnection conn,
82 /* If no client found, ignore the packet, a silent error */
84 silc_fsm_next(context, silc_client_key_agreement_error);
86 /* Continue processing the packet */
87 SILC_FSM_CALL_CONTINUE(context);
90 /* Key exchange completion callback. Called after connected to remote host
91 and performed key exchange, when we are initiator. As responder, this is
92 called after the remote has connected to us and have performed the key
95 static void silc_client_keyagr_completion(SilcClient client,
96 SilcClientConnection conn,
97 SilcClientConnectionStatus status,
102 SilcClientEntry client_entry = context;
103 SilcClientKeyAgreement ke = client_entry->internal.ke;
104 SilcSKEKeyMaterial keymat;
106 client_entry->internal.op = NULL;
109 case SILC_CLIENT_CONN_SUCCESS:
110 SILC_LOG_DEBUG(("Key agreement %p successful", ke));
112 keymat = silc_ske_get_key_material(conn->internal->ske);
113 ke->completion(ke->client, ke->conn, client_entry, SILC_KEY_AGREEMENT_OK,
114 keymat, ke->context);
117 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
118 SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
119 ke->completion(ke->client, ke->conn, client_entry,
120 SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
124 SILC_LOG_DEBUG(("Key agreement %p error %d", ke, status));
125 ke->completion(ke->client, ke->conn, client_entry,
126 SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
130 /* Close the connection */
132 silc_client_close_connection(ke->client, conn);
134 silc_client_keyagr_free(ke->client, ke->conn, client_entry);
137 /*************************** Key Agreement API ******************************/
139 /* Sends key agreement packet to remote client. If IP addresses are provided
140 creates also listener for íncoming key agreement connection. Supports
141 both TCP and UDP transports. */
143 void silc_client_send_key_agreement(SilcClient client,
144 SilcClientConnection conn,
145 SilcClientEntry client_entry,
146 SilcClientConnectionParams *params,
147 SilcPublicKey public_key,
148 SilcPrivateKey private_key,
149 SilcKeyAgreementCallback completion,
152 SilcClientKeyAgreement ke = NULL;
154 SilcUInt16 port = 0, protocol = 0;
155 char *local_ip = NULL;
157 SILC_LOG_DEBUG(("Sending key agreement"));
161 if (conn->internal->disconnected)
164 if (client_entry->internal.ke) {
165 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED,
170 if (client_entry == conn->local_entry) {
171 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
176 /* If local IP is provided, create listener. If this is not provided,
177 we'll just send empty key agreement payload */
178 if (params && (params->local_ip || params->bind_ip)) {
179 ke = silc_calloc(1, sizeof(*ke));
181 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
186 /* Create listener */
187 ke->listener = silc_client_listener_add(client, conn->internal->schedule,
188 params, public_key, private_key,
189 silc_client_keyagr_completion,
192 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
197 local_ip = params->local_ip;
198 protocol = params->udp;
202 ke->completion = completion;
203 ke->context = context;
204 silc_client_ref_client(client, conn, client_entry);
205 client_entry->internal.ke = ke;
206 client_entry->internal.prv_resp = TRUE;
209 /* Encode the key agreement payload */
210 buffer = silc_key_agreement_payload_encode(local_ip, protocol, port);
212 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
214 silc_client_keyagr_free(client, conn, client_entry);
218 /* Send the key agreement packet to the client */
219 if (!silc_packet_send_ext(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0,
220 0, NULL, SILC_ID_CLIENT, &client_entry->id,
221 silc_buffer_datalen(buffer), NULL, NULL)) {
222 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
224 silc_client_keyagr_free(client, conn, client_entry);
225 silc_buffer_free(buffer);
229 /* Add key agreement timeout task */
230 if (params && params->timeout_secs)
231 silc_schedule_task_add_timeout(conn->internal->schedule,
232 silc_client_keyagr_timeout,
233 client_entry, params->timeout_secs, 0);
235 silc_buffer_free(buffer);
238 /* Perform key agreement protocol as initiator. Conneects to remote host. */
240 void silc_client_perform_key_agreement(SilcClient client,
241 SilcClientConnection conn,
242 SilcClientEntry client_entry,
243 SilcClientConnectionParams *params,
244 SilcPublicKey public_key,
245 SilcPrivateKey private_key,
246 char *hostname, int port,
247 SilcKeyAgreementCallback completion,
250 SilcClientKeyAgreement ke;
252 SILC_LOG_DEBUG(("Performing key agreement"));
254 if (!client_entry || !hostname || !port) {
255 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
260 if (client_entry == conn->local_entry) {
261 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
266 ke = silc_calloc(1, sizeof(*ke));
268 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
274 ke->completion = completion;
275 ke->context = context;
276 silc_client_ref_client(client, conn, client_entry);
277 client_entry->internal.ke = ke;
280 params->no_authentication = TRUE;
282 /* Connect to the remote client. Performs key exchange automatically. */
283 client_entry->internal.op =
284 silc_client_connect_to_client(client, params, public_key,
285 private_key, hostname, port,
286 silc_client_keyagr_completion,
288 if (!client_entry->internal.op) {
289 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
291 silc_client_keyagr_free(client, conn, client_entry);
296 /* Same as above but caller has created connection. */
299 silc_client_perform_key_agreement_stream(SilcClient client,
300 SilcClientConnection conn,
301 SilcClientEntry client_entry,
302 SilcClientConnectionParams *params,
303 SilcPublicKey public_key,
304 SilcPrivateKey private_key,
306 SilcKeyAgreementCallback completion,
309 SilcClientKeyAgreement ke;
311 SILC_LOG_DEBUG(("Performing key agreement"));
313 if (!client_entry || !stream) {
314 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
319 if (client_entry == conn->local_entry) {
320 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
325 ke = silc_calloc(1, sizeof(*ke));
327 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
333 ke->completion = completion;
334 ke->context = context;
335 silc_client_ref_client(client, conn, client_entry);
336 client_entry->internal.ke = ke;
339 params->no_authentication = TRUE;
341 /* Perform key exchange protocol */
342 client_entry->internal.op =
343 silc_client_key_exchange(client, params, public_key,
344 private_key, stream, SILC_CONN_CLIENT,
345 silc_client_keyagr_completion,
347 if (!client_entry->internal.op) {
348 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
350 silc_client_keyagr_free(client, conn, client_entry);
355 /* This function can be called to unbind the hostname and the port for
356 the key agreement protocol. However, this function has effect only
357 before the key agreement protocol has been performed. After it has
358 been performed the library will automatically unbind the port. The
359 `client_entry' is the client to which we sent the key agreement
362 void silc_client_abort_key_agreement(SilcClient client,
363 SilcClientConnection conn,
364 SilcClientEntry client_entry)
366 SilcClientKeyAgreement ke;
368 if (!client_entry || !client_entry->internal.ke)
371 ke = client_entry->internal.ke;
373 SILC_LOG_DEBUG(("Abort key agreement ke %p for client %p on connection %p",
376 ke->completion(client, conn, client_entry,
377 SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
379 silc_client_keyagr_free(client, conn, client_entry);
382 /* Key agreement packet received */
384 SILC_FSM_STATE(silc_client_key_agreement)
386 SilcClientConnection conn = fsm_context;
387 SilcClient client = conn->client;
388 SilcPacket packet = state_context;
389 SilcClientID remote_id;
390 SilcClientEntry remote_client;
391 SilcKeyAgreementPayload payload;
393 if (packet->src_id_type != SILC_ID_CLIENT) {
394 /** Invalid packet */
395 silc_fsm_next(fsm, silc_client_key_agreement_error);
396 return SILC_FSM_CONTINUE;
399 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
400 &remote_id, sizeof(remote_id))) {
401 /** Invalid source ID */
402 silc_fsm_next(fsm, silc_client_key_agreement_error);
403 return SILC_FSM_CONTINUE;
406 /* Check whether we know this client already */
407 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
408 if (!remote_client || !remote_client->internal.valid) {
409 /** Resolve client info */
410 silc_client_unref_client(client, conn, remote_client);
411 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
412 client, conn, &remote_id, NULL,
413 silc_client_keyagr_resolved, fsm));
417 /* Parse the key agreement payload */
418 payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer),
419 silc_buffer_len(&packet->buffer));
421 /** Malformed Payload */
422 SILC_LOG_DEBUG(("Malformed key agreement payload"));
423 silc_fsm_next(fsm, silc_client_key_agreement_error);
424 return SILC_FSM_CONTINUE;
427 /* If remote did not provide connection endpoint, we will assume that we
428 will provide it and will be responder. */
429 if (!silc_key_agreement_get_hostname(payload))
430 remote_client->internal.prv_resp = TRUE;
432 remote_client->internal.prv_resp = FALSE;
434 /* Notify application for key agreement request */
435 client->internal->ops->key_agreement(
436 client, conn, remote_client,
437 silc_key_agreement_get_hostname(payload),
438 silc_key_agreement_get_protocol(payload),
439 silc_key_agreement_get_port(payload));
441 silc_key_agreement_payload_free(payload);
443 silc_packet_free(packet);
444 return SILC_FSM_FINISH;
447 /* Key agreement packet processing error */
449 SILC_FSM_STATE(silc_client_key_agreement_error)
451 SilcPacket packet = state_context;
452 silc_packet_free(packet);
453 return SILC_FSM_FINISH;