5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2006 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 /************************ Static utility functions **************************/
26 /* Callback called after connected to remote host */
28 static void silc_client_connect_callback(SilcNetStatus status,
29 SilcStream stream, void *context)
31 SilcFSMThread fsm = context;
32 SilcClientConnection conn = silc_fsm_get_context(fsm);
33 SilcClient client = conn->client;
35 conn->internal->op = NULL;
36 if (conn->internal->verbose) {
40 case SILC_NET_UNKNOWN_IP:
41 client->internal->ops->say(
42 client, conn, SILC_CLIENT_MESSAGE_ERROR,
43 "Could not connect to host %s: unknown IP address",
46 case SILC_NET_UNKNOWN_HOST:
47 client->internal->ops->say(
48 client, conn, SILC_CLIENT_MESSAGE_ERROR,
49 "Could not connect to host %s: unknown host name",
52 case SILC_NET_HOST_UNREACHABLE:
53 client->internal->ops->say(
54 client, conn, SILC_CLIENT_MESSAGE_ERROR,
55 "Could not connect to host %s: network unreachable",
58 case SILC_NET_CONNECTION_REFUSED:
59 client->internal->ops->say(
60 client, conn, SILC_CLIENT_MESSAGE_ERROR,
61 "Could not connect to host %s: connection refused",
64 case SILC_NET_CONNECTION_TIMEOUT:
65 client->internal->ops->say(
66 client, conn, SILC_CLIENT_MESSAGE_ERROR,
67 "Could not connect to host %s: connection timeout",
71 client->internal->ops->say(
72 client, conn, SILC_CLIENT_MESSAGE_ERROR,
73 "Could not connect to host %s",
79 if (status != SILC_NET_OK) {
80 /* Notify application of failure */
81 SILC_LOG_DEBUG(("Connecting failed"));
82 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0,
83 NULL, conn->callback_context);
84 silc_fsm_next(fsm, silc_client_st_connect_error);
85 SILC_FSM_CALL_CONTINUE(fsm);
89 /* Connection created successfully */
90 SILC_LOG_DEBUG(("Connected"));
91 conn->stream = (void *)stream;
92 SILC_FSM_CALL_CONTINUE(fsm);
95 /* Called after application has verified remote host's public key */
97 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
99 VerifyKeyContext verify = (VerifyKeyContext)context;
101 SILC_LOG_DEBUG(("Start"));
103 /* Call the completion callback back to the SKE */
104 verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
105 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
106 verify->completion_context);
111 /* Verify remote host's public key */
113 static void silc_client_ke_verify_key(SilcSKE ske,
114 SilcPublicKey public_key,
116 SilcSKEVerifyCbCompletion completion,
117 void *completion_context)
119 SilcFSMThread fsm = context;
120 SilcClientConnection conn = silc_fsm_get_context(fsm);
121 SilcClient client = conn->client;
122 VerifyKeyContext verify;
124 /* If we provided repository for SKE and we got here the key was not
125 found from the repository. */
126 if (conn->internal->params.repository &&
127 !conn->internal->params.verify_notfound) {
128 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
133 SILC_LOG_DEBUG(("Verify remote public key"));
135 verify = silc_calloc(1, sizeof(*verify));
137 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
142 verify->completion = completion;
143 verify->completion_context = completion_context;
145 /* Verify public key in application */
146 client->internal->ops->verify_public_key(client, conn,
147 conn->type, public_key,
148 silc_client_ke_verify_key_cb,
152 /* Key exchange protocol completion callback */
154 static void silc_client_ke_completion(SilcSKE ske,
155 SilcSKEStatus status,
156 SilcSKESecurityProperties prop,
157 SilcSKEKeyMaterial keymat,
158 SilcSKERekeyMaterial rekey,
161 SilcFSMThread fsm = context;
162 SilcClientConnection conn = silc_fsm_get_context(fsm);
163 SilcClient client = conn->client;
164 SilcCipher send_key, receive_key;
165 SilcHmac hmac_send, hmac_receive;
167 conn->internal->op = NULL;
168 if (status != SILC_SKE_STATUS_OK) {
169 /* Key exchange failed */
170 SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
171 conn->remote_host, silc_ske_map_status(status), status));
173 if (conn->internal->verbose)
174 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
175 "Error during key exchange with %s: %s",
177 silc_ske_map_status(status));
179 silc_ske_free_rekey_material(rekey);
181 silc_fsm_next(fsm, silc_client_st_connect_error);
182 SILC_FSM_CALL_CONTINUE(fsm);
186 SILC_LOG_DEBUG(("Setting keys into use"));
188 /* Allocate the cipher and HMAC contexts */
189 if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
190 &hmac_send, &hmac_receive, &conn->internal->hash)) {
191 /* Error setting keys */
192 SILC_LOG_DEBUG(("Could not set keys into use"));
194 if (conn->internal->verbose)
195 client->internal->ops->say(
196 client, conn, SILC_CLIENT_MESSAGE_ERROR,
197 "Error during key exchange with %s: cannot use keys",
200 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
201 conn->callback_context);
203 silc_ske_free_rekey_material(rekey);
205 silc_fsm_next(fsm, silc_client_st_connect_error);
206 SILC_FSM_CALL_CONTINUE(fsm);
210 /* Set the keys into the packet stream. After this call packets will be
211 encrypted with these keys. */
212 if (!silc_packet_set_keys(conn->stream, send_key, receive_key, hmac_send,
213 hmac_receive, FALSE)) {
214 /* Error setting keys */
215 SILC_LOG_DEBUG(("Could not set keys into use"));
217 if (conn->internal->verbose)
218 client->internal->ops->say(
219 client, conn, SILC_CLIENT_MESSAGE_ERROR,
220 "Error during key exchange with %s: cannot use keys",
223 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
224 conn->callback_context);
226 silc_ske_free_rekey_material(rekey);
228 silc_fsm_next(fsm, silc_client_st_connect_error);
229 SILC_FSM_CALL_CONTINUE(fsm);
233 conn->internal->rekey = rekey;
235 SILC_LOG_DEBUG(("Key Exchange completed"));
237 /* Key exchange done */
238 SILC_FSM_CALL_CONTINUE(fsm);
241 /* Rekey protocol completion callback */
243 static void silc_client_rekey_completion(SilcSKE ske,
244 SilcSKEStatus status,
245 SilcSKESecurityProperties prop,
246 SilcSKEKeyMaterial keymat,
247 SilcSKERekeyMaterial rekey,
250 SilcFSMThread fsm = context;
251 SilcClientConnection conn = silc_fsm_get_context(fsm);
252 SilcClient client = conn->client;
254 conn->internal->op = NULL;
255 if (status != SILC_SKE_STATUS_OK) {
257 SILC_LOG_DEBUG(("Error during rekey with %s: %s (%d)",
258 conn->remote_host, silc_ske_map_status(status), status));
260 if (conn->internal->verbose)
261 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
262 "Error during rekey with %s: %s",
264 silc_ske_map_status(status));
266 silc_fsm_finish(fsm);
270 silc_ske_free_rekey_material(conn->internal->rekey);
271 conn->internal->rekey = rekey;
273 SILC_LOG_DEBUG(("Rekey completed"));
276 silc_fsm_finish(fsm);
279 /* Callback called by application to return authentication data */
281 static void silc_client_connect_auth_method(SilcBool success,
282 SilcAuthMethod auth_meth,
283 void *auth, SilcUInt32 auth_len,
286 SilcFSMThread fsm = context;
287 SilcClientConnection conn = silc_fsm_get_context(fsm);
289 conn->internal->params.auth_method = SILC_AUTH_NONE;
292 conn->internal->params.auth_method = auth_meth;
293 conn->internal->params.auth = auth;
294 conn->internal->params.auth_len = auth_len;
297 SILC_FSM_CALL_CONTINUE(fsm);
300 /* Connection authentication completion callback */
302 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
306 SilcFSMThread fsm = context;
307 SilcClientConnection conn = silc_fsm_get_context(fsm);
308 SilcClient client = conn->client;
310 conn->internal->op = NULL;
311 silc_connauth_free(connauth);
314 if (conn->internal->verbose)
315 client->internal->ops->say(
316 client, conn, SILC_CLIENT_MESSAGE_ERROR,
317 "Authentication failed");
319 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
320 conn->callback_context);
321 silc_fsm_next(fsm, silc_client_st_connect_error);
324 SILC_FSM_CALL_CONTINUE(fsm);
327 /*************************** Connect remote host ****************************/
329 /* Connection timeout callback */
331 SILC_TASK_CALLBACK(silc_client_connect_timeout)
333 SilcClientConnection conn = context;
334 SilcClient client = conn->client;
336 SILC_LOG_DEBUG(("Connection timeout"));
338 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_TIMEOUT, 0, NULL,
339 conn->callback_context);
341 silc_fsm_next(&conn->internal->event_thread, silc_client_st_connect_error);
342 silc_fsm_continue_sync(&conn->internal->event_thread);
345 /* Creates a connection to remote host */
347 SILC_FSM_STATE(silc_client_st_connect)
349 SilcClientConnection conn = fsm_context;
350 SilcClient client = conn->client;
352 SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
356 silc_fsm_next(fsm, silc_client_st_connect_set_stream);
358 /* Add connection timeout */
359 if (conn->internal->params.timeout_secs)
360 silc_schedule_task_add_timeout(conn->internal->schedule,
361 silc_client_connect_timeout, conn,
362 conn->internal->params.timeout_secs, 0);
364 if (conn->internal->params.udp) {
367 if (!conn->internal->params.local_ip) {
368 /** IP address not given */
369 SILC_LOG_ERROR(("Local UDP IP address not specified"));
370 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
371 conn->callback_context);
372 silc_fsm_next(fsm, silc_client_st_connect_error);
377 stream = silc_net_udp_connect(conn->internal->params.bind_ip ?
378 conn->internal->params.bind_ip :
379 conn->internal->params.local_ip,
380 conn->internal->params.local_port,
381 conn->remote_host, conn->remote_port,
382 conn->internal->schedule);
384 SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
385 SILC_NET_HOST_UNREACHABLE,
389 SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect(
390 NULL, conn->remote_host,
392 conn->internal->schedule,
393 silc_client_connect_callback, fsm));
397 /* Sets the new connection stream into use and creates packet stream */
399 SILC_FSM_STATE(silc_client_st_connect_set_stream)
401 SilcClientConnection conn = fsm_context;
402 SilcClient client = conn->client;
404 if (conn->internal->disconnected) {
406 silc_fsm_next(fsm, silc_client_st_connect_error);
410 /* Create packet stream */
411 conn->stream = silc_packet_stream_create(client->internal->packet_engine,
412 conn->internal->schedule,
413 (SilcStream)conn->stream);
415 /** Cannot create packet stream */
416 SILC_LOG_DEBUG(("Could not create packet stream"));
417 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
418 conn->callback_context);
419 silc_fsm_next(fsm, silc_client_st_connect_error);
423 silc_packet_set_context(conn->stream, conn);
425 /** Start key exchange */
426 silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
430 /* Starts key exchange protocol with remote host */
432 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
434 SilcClientConnection conn = fsm_context;
435 SilcClient client = conn->client;
436 SilcSKEParamsStruct params;
438 SILC_LOG_DEBUG(("Starting key exchange protocol"));
441 conn->internal->ske =
442 silc_ske_alloc(client->rng, conn->internal->schedule,
443 conn->internal->params.repository,
444 conn->public_key, conn->private_key, fsm);
445 if (!conn->internal->ske) {
447 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
448 conn->callback_context);
449 silc_fsm_next(fsm, silc_client_st_connect_error);
453 /* Set SKE callbacks */
454 silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
455 silc_client_ke_completion, fsm);
457 /* Set up key exchange parameters */
458 params.version = client->internal->silc_client_version;
459 params.flags = SILC_SKE_SP_FLAG_MUTUAL;
460 if (conn->internal->params.pfs)
461 params.flags |= SILC_SKE_SP_FLAG_PFS;
462 if (conn->internal->params.udp) {
463 params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
464 params.session_port = conn->internal->params.local_port;
467 if (conn->internal->params.no_authentication)
468 /** Run key exchange (no auth) */
469 silc_fsm_next(fsm, silc_client_st_connected);
470 else if (conn->internal->params.udp)
471 /** Run key exchange (UDP)*/
472 silc_fsm_next(fsm, silc_client_st_connect_setup_udp);
474 /** Run key exchange (TCP) */
475 silc_fsm_next(fsm, silc_client_st_connect_auth);
477 SILC_FSM_CALL(conn->internal->op = silc_ske_initiator(conn->internal->ske,
482 /* For UDP/IP connections, set up the UDP session after successful key
485 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
487 SilcClientConnection conn = fsm_context;
488 SilcClient client = conn->client;
489 SilcStream stream, old;
490 SilcSKESecurityProperties prop;
492 SILC_LOG_DEBUG(("Setup UDP SILC session"));
494 if (conn->internal->disconnected) {
496 silc_fsm_next(fsm, silc_client_st_connect_error);
500 /* Create new UDP stream */
501 prop = silc_ske_get_security_properties(conn->internal->ske);
502 stream = silc_net_udp_connect(conn->internal->params.local_ip,
503 conn->internal->params.local_port,
504 conn->remote_host, prop->remote_port,
505 conn->internal->schedule);
507 /** Cannot create UDP stream */
508 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
509 conn->callback_context);
510 silc_fsm_next(fsm, silc_client_st_connect_error);
514 /* Set the new stream to packet stream */
515 old = silc_packet_stream_get_stream(conn->stream);
516 silc_packet_stream_set_stream(conn->stream, stream);
517 silc_packet_stream_set_iv_included(conn->stream);
518 silc_packet_set_sid(conn->stream, 0);
520 /* Delete the old stream */
521 silc_stream_destroy(old);
523 /** Start authentication */
524 silc_fsm_next(fsm, silc_client_st_connect_auth);
528 /* Get authentication method to be used in authentication protocol */
530 SILC_FSM_STATE(silc_client_st_connect_auth)
532 SilcClientConnection conn = fsm_context;
533 SilcClient client = conn->client;
535 SILC_LOG_DEBUG(("Get authentication data"));
537 if (conn->internal->disconnected) {
539 silc_fsm_next(fsm, silc_client_st_connect_error);
543 silc_fsm_next(fsm, silc_client_st_connect_auth_start);
545 /* If authentication data not provided, ask from application */
546 if (!conn->internal->params.auth_set)
547 SILC_FSM_CALL(client->internal->ops->get_auth_method(
551 silc_client_connect_auth_method, fsm));
553 if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
554 conn->internal->params.auth = conn->private_key;
556 /* We have authentication data */
560 /* Start connection authentication with remote host */
562 SILC_FSM_STATE(silc_client_st_connect_auth_start)
564 SilcClientConnection conn = fsm_context;
565 SilcClient client = conn->client;
566 SilcConnAuth connauth;
568 SILC_LOG_DEBUG(("Starting connection authentication protocol"));
570 if (conn->internal->disconnected) {
572 silc_fsm_next(fsm, silc_client_st_connect_error);
576 /* Allocate connection authentication protocol */
577 connauth = silc_connauth_alloc(conn->internal->schedule,
579 conn->internal->params.rekey_secs);
582 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
583 conn->callback_context);
584 silc_fsm_next(fsm, silc_client_st_connect_error);
588 /** Start connection authentication */
589 silc_fsm_next(fsm, silc_client_st_connected);
590 SILC_FSM_CALL(conn->internal->op = silc_connauth_initiator(
591 connauth, SILC_CONN_CLIENT,
592 conn->internal->params.auth_method,
593 conn->internal->params.auth,
594 conn->internal->params.auth_len,
595 silc_client_connect_auth_completion,
599 /* Connection fully established */
601 SILC_FSM_STATE(silc_client_st_connected)
603 SilcClientConnection conn = fsm_context;
604 SilcClient client = conn->client;
606 silc_ske_free(conn->internal->ske);
607 conn->internal->ske = NULL;
609 if (conn->internal->disconnected) {
611 silc_fsm_next(fsm, silc_client_st_connect_error);
615 SILC_LOG_DEBUG(("Connection established"));
617 /* Install rekey timer */
618 silc_schedule_task_add_timeout(conn->internal->schedule,
619 silc_client_rekey_timer, conn,
620 conn->internal->params.rekey_secs, 0);
622 /* If we connected to server, now register to network. */
623 if (conn->type == SILC_CONN_SERVER &&
624 !conn->internal->params.no_authentication) {
626 /* If detach data is provided, resume the session. */
627 if (conn->internal->params.detach_data &&
628 conn->internal->params.detach_data_len) {
629 /** Resume detached session */
630 silc_fsm_next(fsm, silc_client_st_resume);
632 /** Register to network */
633 silc_fsm_next(fsm, silc_client_st_register);
639 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
640 silc_client_connect_timeout, conn);
642 /* Call connection callback */
643 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
644 conn->callback_context);
649 /* Error during connecting */
651 SILC_FSM_STATE(silc_client_st_connect_error)
653 SilcClientConnection conn = fsm_context;
655 if (conn->internal->ske) {
656 silc_ske_free(conn->internal->ske);
657 conn->internal->ske = NULL;
660 /* Signal to close connection */
661 if (!conn->internal->disconnected) {
662 conn->internal->disconnected = TRUE;
663 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
666 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
667 silc_client_connect_timeout, conn);
672 /****************************** Connect rekey *******************************/
674 /* Connection rekey timer callback */
676 SILC_TASK_CALLBACK(silc_client_rekey_timer)
678 SilcClientConnection conn = context;
680 /* Signal to start rekey */
681 conn->internal->rekey_responder = FALSE;
682 conn->internal->rekeying = TRUE;
683 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
685 /* Reinstall rekey timer */
686 silc_schedule_task_add_timeout(conn->internal->schedule,
687 silc_client_rekey_timer, conn,
688 conn->internal->params.rekey_secs, 0);
693 SILC_FSM_STATE(silc_client_st_rekey)
695 SilcClientConnection conn = fsm_context;
696 SilcClient client = conn->client;
698 SILC_LOG_DEBUG(("Rekey"));
700 if (conn->internal->disconnected)
704 conn->internal->ske =
705 silc_ske_alloc(client->rng, conn->internal->schedule,
706 conn->internal->params.repository,
707 conn->public_key, conn->private_key, fsm);
708 if (!conn->internal->ske)
711 /* Set SKE callbacks */
712 silc_ske_set_callbacks(conn->internal->ske, NULL,
713 silc_client_rekey_completion, fsm);
716 if (!conn->internal->rekey_responder)
717 SILC_FSM_CALL(conn->internal->op = silc_ske_rekey_initiator(
720 conn->internal->rekey));
722 SILC_FSM_CALL(conn->internal->op = silc_ske_rekey_responder(
725 conn->internal->rekey));