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 SilcKeyAgreementCallback completion; /* Key agreement completion */
31 void *context; /* User context */
32 SilcClientConnectionParams params; /* Connection parameters */
33 SilcPublicKey public_key; /* Responder public key */
34 SilcPrivateKey private_key; /* Responder private key */
35 SilcNetListener tcp_listener; /* TCP listener */
36 SilcPacketStream udp_listener; /* UDP listener */
37 SilcPacketStream stream; /* Remote connection (TCP or UDP) */
38 SilcAsyncOperation op; /* SKE operation */
39 SilcSKE ske; /* SKE */
42 /************************ Static utility functions **************************/
44 /* Destroyes key agreement session */
46 static void silc_client_keyagr_free(SilcClient client,
47 SilcClientConnection conn,
48 SilcClientEntry client_entry)
50 SilcClientKeyAgreement ke = client_entry->internal.ke;
52 silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
55 silc_async_abort(ke->op, NULL, NULL);
57 silc_ske_free(ke->ske);
59 silc_net_close_listener(ke->tcp_listener);
60 silc_packet_stream_destroy(ke->stream);
61 silc_packet_stream_destroy(ke->udp_listener);
63 client_entry->internal.ke = NULL;
64 client_entry->internal.prv_resp = FALSE;
65 silc_client_unref_client(client, conn, client_entry);
70 /* Key agreement timeout callback */
72 SILC_TASK_CALLBACK(silc_client_keyagr_timeout)
74 SilcClientEntry client_entry = context;
75 SilcClientKeyAgreement ke = client_entry->internal.ke;
77 SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
79 ke->completion(ke->client, ke->conn, client_entry,
80 SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
82 silc_client_keyagr_free(ke->client, ke->conn, client_entry);
85 /* Client resolving callback. Continues with the key agreement processing */
87 static void silc_client_keyagr_resolved(SilcClient client,
88 SilcClientConnection conn,
93 /* If no client found, ignore the packet, a silent error */
95 silc_fsm_next(context, silc_client_key_agreement_error);
97 /* Continue processing the packet */
98 SILC_FSM_CALL_CONTINUE(context);
101 /* Called after application has verified remote host's public key. Responder
104 static void silc_client_keyagr_verify_key_cb(SilcBool success, void *context)
106 SilcVerifyKeyContext verify = context;
108 /* Call the completion callback back to the SKE */
109 verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
110 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
111 verify->completion_context);
116 /* Verify remote host's public key. Responder function. */
118 static void silc_client_keyagr_verify_key(SilcSKE ske,
119 SilcPublicKey public_key,
121 SilcSKEVerifyCbCompletion completion,
122 void *completion_context)
124 SilcClientEntry client_entry = context;
125 SilcClientKeyAgreement ke = client_entry->internal.ke;
126 SilcClientConnection conn = ke->conn;
127 SilcClient client = conn->client;
128 SilcVerifyKeyContext verify;
130 /* If we provided repository for SKE and we got here the key was not
131 found from the repository. */
132 if (ke->params.repository && !ke->params.verify_notfound) {
133 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
138 SILC_LOG_DEBUG(("Verify remote public key"));
140 verify = silc_calloc(1, sizeof(*verify));
142 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
147 verify->completion = completion;
148 verify->completion_context = completion_context;
150 /* Verify public key in application */
151 client->internal->ops->verify_public_key(client, conn,
152 SILC_CONN_CLIENT, public_key,
153 silc_client_keyagr_verify_key_cb,
157 /* Key exchange protocol completion callback. Responder function. */
159 static void silc_client_keyagr_completion(SilcSKE ske,
160 SilcSKEStatus status,
161 SilcSKESecurityProperties prop,
162 SilcSKEKeyMaterial keymat,
163 SilcSKERekeyMaterial rekey,
166 SilcClientEntry client_entry = context;
167 SilcClientKeyAgreement ke = client_entry->internal.ke;
168 SilcClientConnection conn = ke->conn;
169 SilcClient client = conn->client;
171 if (status != SILC_SKE_STATUS_OK) {
172 /* Key exchange failed */
173 ke->completion(client, conn, client_entry,
174 status == SILC_SKE_STATUS_TIMEOUT ?
175 SILC_KEY_AGREEMENT_TIMEOUT :
176 SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
177 silc_client_keyagr_free(client, conn, client_entry);
181 /* Returns the negotiated key material to application. Key agreement
183 ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_OK,
184 keymat, ke->context);
186 silc_client_keyagr_free(client, conn, client_entry);
189 /* Starts key agreement as responder. */
191 static void silc_client_process_key_agreement(SilcClient client,
192 SilcClientConnection conn,
193 SilcClientEntry client_entry)
195 SilcClientKeyAgreement ke = client_entry->internal.ke;
196 SilcSKEParamsStruct params;
198 SILC_LOG_DEBUG(("Processing key agrement %p session", ke));
201 ke->ske = silc_ske_alloc(client->rng, conn->internal->schedule,
202 ke->params.repository, ke->public_key,
203 ke->private_key, client_entry);
205 ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
207 silc_client_keyagr_free(client, conn, client_entry);
211 /* Set SKE parameters */
212 params.version = client->internal->silc_client_version;
213 params.flags = SILC_SKE_SP_FLAG_MUTUAL;
214 if (ke->params.udp) {
215 params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
216 params.session_port = ke->params.local_port;
219 silc_ske_set_callbacks(ke->ske, silc_client_keyagr_verify_key,
220 silc_client_keyagr_completion, client_entry);
222 /* Start key exchange as responder */
223 ke->op = silc_ske_responder(ke->ske, ke->stream, ¶ms);
225 ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
227 silc_client_keyagr_free(client, conn, client_entry);
231 /* TCP network listener callback. Accepts new key agreement connection.
232 Responder function. */
234 static void silc_client_tcp_accept(SilcNetStatus status,
238 SilcClientEntry client_entry = context;
239 SilcClientKeyAgreement ke = client_entry->internal.ke;
241 /* Create packet stream */
242 ke->stream = silc_packet_stream_create(ke->client->internal->packet_engine,
243 ke->conn->internal->schedule, stream);
245 silc_stream_destroy(stream);
249 /* Process session */
250 silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
253 /* UDP network listener callback. Accepts new key agreement session.
254 Responder function. */
256 static SilcBool silc_client_udp_accept(SilcPacketEngine engine,
257 SilcPacketStream stream,
259 void *callback_context,
260 void *stream_context)
262 SilcClientEntry client_entry = callback_context;
263 SilcClientKeyAgreement ke = client_entry->internal.ke;
267 /* We want only key exchange packet. Eat other packets so that default
268 packet callback doesn't get them. */
269 if (packet->type != SILC_PACKET_KEY_EXCHANGE) {
270 silc_packet_free(packet);
274 /* Create packet stream for this remote UDP session */
275 if (!silc_packet_get_sender(packet, &ip, &port)) {
276 silc_packet_free(packet);
279 ke->stream = silc_packet_stream_add_remote(stream, ip, port, packet);
281 silc_packet_free(packet);
285 /* Process session */
286 silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
290 /* Client connect completion callback. Initiator function. */
292 static void silc_client_keyagr_perform_cb(SilcClient client,
293 SilcClientConnection conn,
294 SilcClientConnectionStatus status,
299 SilcClientEntry client_entry = context;
300 SilcClientKeyAgreement ke = client_entry->internal.ke;
301 SilcSKEKeyMaterial keymat;
306 case SILC_CLIENT_CONN_SUCCESS:
307 SILC_LOG_DEBUG(("Key agreement %p successful", ke));
309 keymat = silc_ske_get_key_material(conn->internal->ske);
310 ke->completion(ke->client, ke->conn, client_entry, SILC_KEY_AGREEMENT_OK,
311 keymat, ke->context);
314 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
315 SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
316 ke->completion(ke->client, ke->conn, client_entry,
317 SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
321 SILC_LOG_DEBUG(("Key agreement %p error %d", ke, status));
322 ke->completion(ke->client, ke->conn, client_entry,
323 SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
327 /* Close the created connection */
329 silc_client_close_connection(ke->client, conn);
331 silc_client_keyagr_free(ke->client, ke->conn, client_entry);
334 /* Packet stream callbacks */
335 static SilcPacketCallbacks silc_client_keyagr_stream_cb =
337 silc_client_udp_accept, NULL, NULL
340 /*************************** Key Agreement API ******************************/
342 /* Sends key agreement packet to remote client. If IP addresses are provided
343 creates also listener for Ãncoming key agreement connection. Supports
344 both TCP and UDP transports. */
346 void silc_client_send_key_agreement(SilcClient client,
347 SilcClientConnection conn,
348 SilcClientEntry client_entry,
349 SilcClientConnectionParams *params,
350 SilcPublicKey public_key,
351 SilcPrivateKey private_key,
352 SilcKeyAgreementCallback completion,
355 SilcClientKeyAgreement ke = NULL;
357 SilcUInt16 port = 0, protocol = 0;
358 char *local_ip = NULL;
361 SILC_LOG_DEBUG(("Sending key agreement"));
365 if (conn->internal->disconnected)
368 if (client_entry->internal.ke) {
369 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED,
374 if (client_entry == conn->local_entry) {
375 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
380 /* If local IP is provided, create listener */
381 if (params && (params->local_ip || params->bind_ip)) {
382 ke = silc_calloc(1, sizeof(*ke));
384 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
389 /* Create network listener */
392 stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
393 params->local_ip, params->local_port,
394 NULL, 0, conn->internal->schedule);
396 silc_packet_stream_create(client->internal->packet_engine,
397 conn->internal->schedule, stream);
398 if (!ke->udp_listener) {
399 client->internal->ops->say(
400 client, conn, SILC_CLIENT_MESSAGE_ERROR,
401 "Cannot create UDP listener on %s on port %d: %s",
402 params->bind_ip ? params->bind_ip :
403 params->local_ip, params->local_port, strerror(errno));
404 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
407 silc_stream_destroy(stream);
411 silc_packet_stream_link(ke->udp_listener,
412 &silc_client_keyagr_stream_cb,
413 client_entry, 1000000,
414 SILC_PACKET_ANY, -1);
416 port = params->local_port;
418 /* Get listener port */
420 silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
421 port = silc_net_get_local_port(sock);
426 silc_net_tcp_create_listener(params->bind_ip ?
427 (const char **)¶ms->bind_ip :
428 (const char **)¶ms->local_ip,
429 1, params->local_port, FALSE, FALSE,
430 conn->internal->schedule,
431 silc_client_tcp_accept,
433 if (!ke->tcp_listener) {
434 client->internal->ops->say(
435 client, conn, SILC_CLIENT_MESSAGE_ERROR,
436 "Cannot create listener on %s on port %d: %s",
437 params->bind_ip ? params->bind_ip :
438 params->local_ip, params->local_port, strerror(errno));
439 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
445 port = params->local_port;
447 /* Get listener port */
449 ports = silc_net_listener_get_port(ke->tcp_listener, NULL);
455 local_ip = params->local_ip;
456 protocol = params->udp;
460 ke->completion = completion;
461 ke->context = context;
462 ke->params = *params;
463 ke->public_key = public_key;
464 ke->private_key = private_key;
465 silc_client_ref_client(client, conn, client_entry);
466 client_entry->internal.ke = ke;
467 client_entry->internal.prv_resp = TRUE;
470 /* Encode the key agreement payload */
471 buffer = silc_key_agreement_payload_encode(local_ip, protocol, port);
473 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
475 silc_client_keyagr_free(client, conn, client_entry);
479 /* Send the key agreement packet to the client */
480 if (!silc_packet_send_ext(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0,
481 0, NULL, SILC_ID_CLIENT, &client_entry->id,
482 silc_buffer_datalen(buffer), NULL, NULL)) {
483 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
485 silc_client_keyagr_free(client, conn, client_entry);
486 silc_buffer_free(buffer);
490 /* Add key agreement timeout task */
491 if (params && params->timeout_secs)
492 silc_schedule_task_add_timeout(conn->internal->schedule,
493 silc_client_keyagr_timeout,
494 client_entry, params->timeout_secs, 0);
496 silc_buffer_free(buffer);
499 /* Perform key agreement protocol as initiator. Conneects to remote host. */
501 void silc_client_perform_key_agreement(SilcClient client,
502 SilcClientConnection conn,
503 SilcClientEntry client_entry,
504 SilcClientConnectionParams *params,
505 SilcPublicKey public_key,
506 SilcPrivateKey private_key,
507 char *hostname, int port,
508 SilcKeyAgreementCallback completion,
511 SilcClientKeyAgreement ke;
513 SILC_LOG_DEBUG(("Performing key agreement"));
515 if (!client_entry || !hostname || !port) {
516 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
521 if (client_entry == conn->local_entry) {
522 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
527 ke = silc_calloc(1, sizeof(*ke));
529 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
535 ke->completion = completion;
536 ke->context = context;
537 silc_client_ref_client(client, conn, client_entry);
538 client_entry->internal.ke = ke;
541 params->no_authentication = TRUE;
543 /* Connect to the remote client. Performs key exchange automatically. */
544 if (!silc_client_connect_to_client(client, params, public_key,
545 private_key, hostname, port,
546 silc_client_keyagr_perform_cb,
548 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
550 silc_client_keyagr_free(client, conn, client_entry);
555 /* Same as above but caller has created connection. */
558 silc_client_perform_key_agreement_stream(SilcClient client,
559 SilcClientConnection conn,
560 SilcClientEntry client_entry,
561 SilcClientConnectionParams *params,
562 SilcPublicKey public_key,
563 SilcPrivateKey private_key,
565 SilcKeyAgreementCallback completion,
568 SilcClientKeyAgreement ke;
570 SILC_LOG_DEBUG(("Performing key agreement"));
572 if (!client_entry || !stream) {
573 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
578 if (client_entry == conn->local_entry) {
579 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
584 ke = silc_calloc(1, sizeof(*ke));
586 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
592 ke->completion = completion;
593 ke->context = context;
594 silc_client_ref_client(client, conn, client_entry);
595 client_entry->internal.ke = ke;
598 params->no_authentication = TRUE;
600 /* Perform key exchange protocol */
601 if (!silc_client_key_exchange(client, params, public_key,
602 private_key, stream, SILC_CONN_CLIENT,
603 silc_client_keyagr_perform_cb,
605 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
607 silc_client_keyagr_free(client, conn, client_entry);
612 /* This function can be called to unbind the hostname and the port for
613 the key agreement protocol. However, this function has effect only
614 before the key agreement protocol has been performed. After it has
615 been performed the library will automatically unbind the port. The
616 `client_entry' is the client to which we sent the key agreement
619 void silc_client_abort_key_agreement(SilcClient client,
620 SilcClientConnection conn,
621 SilcClientEntry client_entry)
623 SilcClientKeyAgreement ke;
625 if (!client_entry || !client_entry->internal.ke)
628 ke = client_entry->internal.ke;
630 SILC_LOG_DEBUG(("Abort key agreement %p"));
632 ke->completion(client, conn, client_entry,
633 SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
635 silc_client_keyagr_free(client, conn, client_entry);
638 /* Key agreement packet received */
640 SILC_FSM_STATE(silc_client_key_agreement)
642 SilcClientConnection conn = fsm_context;
643 SilcClient client = conn->client;
644 SilcPacket packet = state_context;
645 SilcClientID remote_id;
646 SilcClientEntry remote_client;
647 SilcKeyAgreementPayload payload;
649 if (packet->src_id_type != SILC_ID_CLIENT) {
650 /** Invalid packet */
651 silc_fsm_next(fsm, silc_client_key_agreement_error);
652 return SILC_FSM_CONTINUE;
655 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
656 &remote_id, sizeof(remote_id))) {
657 /** Invalid source ID */
658 silc_fsm_next(fsm, silc_client_key_agreement_error);
659 return SILC_FSM_CONTINUE;
662 /* Check whether we know this client already */
663 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
664 if (!remote_client || !remote_client->internal.valid) {
665 /** Resolve client info */
666 silc_client_unref_client(client, conn, remote_client);
667 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
668 client, conn, &remote_id, NULL,
669 silc_client_keyagr_resolved, fsm));
673 /* Parse the key agreement payload */
674 payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer),
675 silc_buffer_len(&packet->buffer));
677 /** Malformed Payload */
678 SILC_LOG_DEBUG(("Malformed key agreement payload"));
679 silc_fsm_next(fsm, silc_client_key_agreement_error);
680 return SILC_FSM_CONTINUE;
683 /* If remote did not provide connection endpoint, we will assume that we
684 will provide it and will be responder. */
685 if (!silc_key_agreement_get_hostname(payload))
686 remote_client->internal.prv_resp = TRUE;
688 remote_client->internal.prv_resp = FALSE;
690 /* Notify application for key agreement request */
691 client->internal->ops->key_agreement(
692 client, conn, remote_client,
693 silc_key_agreement_get_hostname(payload),
694 silc_key_agreement_get_protocol(payload),
695 silc_key_agreement_get_port(payload));
697 silc_key_agreement_payload_free(payload);
699 silc_packet_free(packet);
700 return SILC_FSM_FINISH;
703 /* Key agreement packet processing error */
705 SILC_FSM_STATE(silc_client_key_agreement_error)
707 SilcPacket packet = state_context;
708 silc_packet_free(packet);
709 return SILC_FSM_FINISH;