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;
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;
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 ke->op = silc_client_connect_to_client(client, params, public_key,
284 private_key, hostname, port,
285 silc_client_keyagr_completion,
288 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
290 silc_client_keyagr_free(client, conn, client_entry);
295 /* Same as above but caller has created connection. */
298 silc_client_perform_key_agreement_stream(SilcClient client,
299 SilcClientConnection conn,
300 SilcClientEntry client_entry,
301 SilcClientConnectionParams *params,
302 SilcPublicKey public_key,
303 SilcPrivateKey private_key,
305 SilcKeyAgreementCallback completion,
308 SilcClientKeyAgreement ke;
310 SILC_LOG_DEBUG(("Performing key agreement"));
312 if (!client_entry || !stream) {
313 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
318 if (client_entry == conn->local_entry) {
319 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
324 ke = silc_calloc(1, sizeof(*ke));
326 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
332 ke->completion = completion;
333 ke->context = context;
334 silc_client_ref_client(client, conn, client_entry);
335 client_entry->internal.ke = ke;
338 params->no_authentication = TRUE;
340 /* Perform key exchange protocol */
341 ke->op = silc_client_key_exchange(client, params, public_key,
342 private_key, stream, SILC_CONN_CLIENT,
343 silc_client_keyagr_completion,
346 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
348 silc_client_keyagr_free(client, conn, client_entry);
353 /* This function can be called to unbind the hostname and the port for
354 the key agreement protocol. However, this function has effect only
355 before the key agreement protocol has been performed. After it has
356 been performed the library will automatically unbind the port. The
357 `client_entry' is the client to which we sent the key agreement
360 void silc_client_abort_key_agreement(SilcClient client,
361 SilcClientConnection conn,
362 SilcClientEntry client_entry)
364 SilcClientKeyAgreement ke;
366 if (!client_entry || !client_entry->internal.ke)
369 ke = client_entry->internal.ke;
371 SILC_LOG_DEBUG(("Abort key agreement ke %p for client %p on connection %p",
374 ke->completion(client, conn, client_entry,
375 SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
377 silc_client_keyagr_free(client, conn, client_entry);
380 /* Key agreement packet received */
382 SILC_FSM_STATE(silc_client_key_agreement)
384 SilcClientConnection conn = fsm_context;
385 SilcClient client = conn->client;
386 SilcPacket packet = state_context;
387 SilcClientID remote_id;
388 SilcClientEntry remote_client;
389 SilcKeyAgreementPayload payload;
391 if (packet->src_id_type != SILC_ID_CLIENT) {
392 /** Invalid packet */
393 silc_fsm_next(fsm, silc_client_key_agreement_error);
394 return SILC_FSM_CONTINUE;
397 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
398 &remote_id, sizeof(remote_id))) {
399 /** Invalid source ID */
400 silc_fsm_next(fsm, silc_client_key_agreement_error);
401 return SILC_FSM_CONTINUE;
404 /* Check whether we know this client already */
405 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
406 if (!remote_client || !remote_client->internal.valid) {
407 /** Resolve client info */
408 silc_client_unref_client(client, conn, remote_client);
409 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
410 client, conn, &remote_id, NULL,
411 silc_client_keyagr_resolved, fsm));
415 /* Parse the key agreement payload */
416 payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer),
417 silc_buffer_len(&packet->buffer));
419 /** Malformed Payload */
420 SILC_LOG_DEBUG(("Malformed key agreement payload"));
421 silc_fsm_next(fsm, silc_client_key_agreement_error);
422 return SILC_FSM_CONTINUE;
425 /* If remote did not provide connection endpoint, we will assume that we
426 will provide it and will be responder. */
427 if (!silc_key_agreement_get_hostname(payload))
428 remote_client->internal.prv_resp = TRUE;
430 remote_client->internal.prv_resp = FALSE;
432 /* Notify application for key agreement request */
433 client->internal->ops->key_agreement(
434 client, conn, remote_client,
435 silc_key_agreement_get_hostname(payload),
436 silc_key_agreement_get_protocol(payload),
437 silc_key_agreement_get_port(payload));
439 silc_key_agreement_payload_free(payload);
441 silc_packet_free(packet);
442 return SILC_FSM_FINISH;
445 /* Key agreement packet processing error */
447 SILC_FSM_STATE(silc_client_key_agreement_error)
449 SilcPacket packet = state_context;
450 silc_packet_free(packet);
451 return SILC_FSM_FINISH;