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 /************************** Types and definitions ***************************/
26 /************************ Static utility functions **************************/
28 /* Callback called after connected to remote host */
30 static void silc_client_connect_callback(SilcNetStatus status,
31 SilcStream stream, void *context)
33 SilcFSMThread fsm = context;
34 SilcClientConnection conn = silc_fsm_get_context(fsm);
35 SilcClient client = conn->client;
37 conn->internal->op = NULL;
38 if (conn->internal->verbose) {
42 case SILC_NET_UNKNOWN_IP:
43 client->internal->ops->say(
44 client, conn, SILC_CLIENT_MESSAGE_ERROR,
45 "Could not connect to host %s: unknown IP address",
48 case SILC_NET_UNKNOWN_HOST:
49 client->internal->ops->say(
50 client, conn, SILC_CLIENT_MESSAGE_ERROR,
51 "Could not connect to host %s: unknown host name",
54 case SILC_NET_HOST_UNREACHABLE:
55 client->internal->ops->say(
56 client, conn, SILC_CLIENT_MESSAGE_ERROR,
57 "Could not connect to host %s: network unreachable",
60 case SILC_NET_CONNECTION_REFUSED:
61 client->internal->ops->say(
62 client, conn, SILC_CLIENT_MESSAGE_ERROR,
63 "Could not connect to host %s: connection refused",
66 case SILC_NET_CONNECTION_TIMEOUT:
67 client->internal->ops->say(
68 client, conn, SILC_CLIENT_MESSAGE_ERROR,
69 "Could not connect to host %s: connection timeout",
73 client->internal->ops->say(
74 client, conn, SILC_CLIENT_MESSAGE_ERROR,
75 "Could not connect to host %s",
81 if (status != SILC_NET_OK) {
82 /* Notify application of failure */
83 SILC_LOG_DEBUG(("Connecting failed"));
84 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0,
85 NULL, conn->callback_context);
86 silc_fsm_next(fsm, silc_client_st_connect_error);
87 SILC_FSM_CALL_CONTINUE(fsm);
91 /* Connection created successfully */
92 SILC_LOG_DEBUG(("Connected"));
93 conn->stream = (void *)stream;
94 SILC_FSM_CALL_CONTINUE(fsm);
97 /* Called after application has verified remote host's public key */
99 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
101 VerifyKeyContext verify = (VerifyKeyContext)context;
103 SILC_LOG_DEBUG(("Start"));
105 /* Call the completion callback back to the SKE */
106 verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
107 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
108 verify->completion_context);
113 /* Verify remote host's public key */
115 static void silc_client_ke_verify_key(SilcSKE ske,
116 SilcPublicKey public_key,
118 SilcSKEVerifyCbCompletion completion,
119 void *completion_context)
121 SilcFSMThread fsm = context;
122 SilcClientConnection conn = silc_fsm_get_context(fsm);
123 SilcClient client = conn->client;
124 VerifyKeyContext verify;
126 /* If we provided repository for SKE and we got here the key was not
127 found from the repository. */
128 if (conn->internal->params.repository &&
129 !conn->internal->params.verify_notfound) {
130 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
135 SILC_LOG_DEBUG(("Verify remote public key"));
137 verify = silc_calloc(1, sizeof(*verify));
139 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
144 verify->completion = completion;
145 verify->completion_context = completion_context;
147 /* Verify public key in application */
148 client->internal->ops->verify_public_key(client, conn,
149 conn->type, public_key,
150 silc_client_ke_verify_key_cb,
154 /* Key exchange protocol completion callback */
156 static void silc_client_ke_completion(SilcSKE ske,
157 SilcSKEStatus status,
158 SilcSKESecurityProperties prop,
159 SilcSKEKeyMaterial keymat,
160 SilcSKERekeyMaterial rekey,
163 SilcFSMThread fsm = context;
164 SilcClientConnection conn = silc_fsm_get_context(fsm);
165 SilcClient client = conn->client;
166 SilcCipher send_key, receive_key;
167 SilcHmac hmac_send, hmac_receive;
169 conn->internal->op = NULL;
170 if (status != SILC_SKE_STATUS_OK) {
171 /* Key exchange failed */
172 SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
173 conn->remote_host, silc_ske_map_status(status), status));
175 if (conn->internal->verbose)
176 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
177 "Error during key exchange with %s: %s",
179 silc_ske_map_status(status));
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 /* Set the keys into use. Data will be encrypted after this. */
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_fsm_next(fsm, silc_client_st_connect_error);
204 SILC_FSM_CALL_CONTINUE(fsm);
208 silc_packet_set_ciphers(conn->stream, send_key, receive_key);
209 silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
211 conn->internal->rekey = rekey;
213 /* Key exchange done */
214 SILC_FSM_CALL_CONTINUE(fsm);
217 /* Callback called by application to return authentication data */
219 static void silc_client_connect_auth_method(SilcBool success,
220 SilcAuthMethod auth_meth,
221 void *auth, SilcUInt32 auth_len,
224 SilcFSMThread fsm = context;
225 SilcClientConnection conn = silc_fsm_get_context(fsm);
227 conn->internal->params.auth_method = SILC_AUTH_NONE;
230 conn->internal->params.auth_method = auth_meth;
231 conn->internal->params.auth = auth;
232 conn->internal->params.auth_len = auth_len;
235 SILC_FSM_CALL_CONTINUE(fsm);
238 /* Connection authentication completion callback */
240 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
244 SilcFSMThread fsm = context;
245 SilcClientConnection conn = silc_fsm_get_context(fsm);
246 SilcClient client = conn->client;
248 silc_connauth_free(connauth);
251 if (conn->internal->verbose)
252 client->internal->ops->say(
253 client, conn, SILC_CLIENT_MESSAGE_ERROR,
254 "Authentication failed");
256 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
257 conn->callback_context);
258 silc_fsm_next(fsm, silc_client_st_connect_error);
261 SILC_FSM_CALL_CONTINUE(fsm);
264 /* Connection timeout callback */
266 SILC_TASK_CALLBACK(silc_client_connect_timeout)
268 SilcClientConnection conn = context;
269 SilcClient client = conn->client;
271 SILC_LOG_DEBUG(("Connection timeout"));
273 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_TIMEOUT, 0, NULL,
274 conn->callback_context);
276 silc_fsm_next(&conn->internal->event_thread, silc_client_st_connect_error);
277 silc_fsm_continue_sync(&conn->internal->event_thread);
280 /*************************** Connect remote host ****************************/
282 /* Creates a connection to remote host */
284 SILC_FSM_STATE(silc_client_st_connect)
286 SilcClientConnection conn = fsm_context;
287 SilcClient client = conn->client;
289 SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
293 silc_fsm_next(fsm, silc_client_st_connect_set_stream);
295 /* Add connection timeout */
296 if (conn->internal->params.timeout_secs)
297 silc_schedule_task_add_timeout(conn->internal->schedule,
298 silc_client_connect_timeout, conn,
299 conn->internal->params.timeout_secs, 0);
301 if (conn->internal->params.udp) {
304 if (!conn->internal->params.local_ip) {
305 /** IP address not given */
306 SILC_LOG_ERROR(("Local UDP IP address not specified"));
307 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
308 conn->callback_context);
309 silc_fsm_next(fsm, silc_client_st_connect_error);
310 return SILC_FSM_CONTINUE;
314 stream = silc_net_udp_connect(conn->internal->params.bind_ip ?
315 conn->internal->params.bind_ip :
316 conn->internal->params.local_ip,
317 conn->internal->params.local_port,
318 conn->remote_host, conn->remote_port,
319 conn->internal->schedule);
321 SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
322 SILC_NET_HOST_UNREACHABLE,
326 SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect(
327 NULL, conn->remote_host,
329 conn->internal->schedule,
330 silc_client_connect_callback, fsm));
334 /* Sets the new connection stream into use and creates packet stream */
336 SILC_FSM_STATE(silc_client_st_connect_set_stream)
338 SilcClientConnection conn = fsm_context;
339 SilcClient client = conn->client;
341 /* Create packet stream */
342 conn->stream = silc_packet_stream_create(client->internal->packet_engine,
343 conn->internal->schedule,
344 (SilcStream)conn->stream);
346 /** Cannot create packet stream */
347 SILC_LOG_DEBUG(("Could not create packet stream"));
348 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
349 conn->callback_context);
350 silc_fsm_next(fsm, silc_client_st_connect_error);
351 return SILC_FSM_CONTINUE;
354 silc_packet_set_context(conn->stream, conn);
356 /** Start key exchange */
357 silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
358 return SILC_FSM_CONTINUE;
361 /* Starts key exchange protocol with remote host */
363 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
365 SilcClientConnection conn = fsm_context;
366 SilcClient client = conn->client;
367 SilcSKEParamsStruct params;
369 SILC_LOG_DEBUG(("Starting key exchange protocol"));
372 conn->internal->ske =
373 silc_ske_alloc(client->rng, conn->internal->schedule,
374 conn->internal->params.repository,
375 conn->public_key, conn->private_key, fsm);
376 if (!conn->internal->ske) {
378 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
379 conn->callback_context);
380 silc_fsm_next(fsm, silc_client_st_connect_error);
381 return SILC_FSM_CONTINUE;
384 /* Set SKE callbacks */
385 silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
386 silc_client_ke_completion, fsm);
388 /* Set up key exchange parameters */
389 params.version = client->internal->silc_client_version;
390 params.flags = SILC_SKE_SP_FLAG_MUTUAL;
391 if (conn->internal->params.pfs)
392 params.flags |= SILC_SKE_SP_FLAG_PFS;
393 if (conn->internal->params.udp) {
394 params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
395 params.session_port = conn->internal->params.local_port;
398 if (conn->internal->params.no_authentication)
399 /** Run key exchange (no auth) */
400 silc_fsm_next(fsm, silc_client_st_connected);
401 else if (conn->internal->params.udp)
402 /** Run key exchange (UDP)*/
403 silc_fsm_next(fsm, silc_client_st_connect_setup_udp);
405 /** Run key exchange (TCP) */
406 silc_fsm_next(fsm, silc_client_st_connect_auth);
408 SILC_FSM_CALL(conn->internal->op = silc_ske_initiator(conn->internal->ske,
413 /* For UDP/IP connections, set up the UDP session after successful key
416 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
418 SilcClientConnection conn = fsm_context;
419 SilcClient client = conn->client;
420 SilcStream stream, old;
421 SilcSKESecurityProperties prop;
423 SILC_LOG_DEBUG(("Setup UDP SILC session"));
425 /* Create new UDP stream */
426 prop = silc_ske_get_security_properties(conn->internal->ske);
427 stream = silc_net_udp_connect(conn->internal->params.local_ip,
428 conn->internal->params.local_port,
429 conn->remote_host, prop->remote_port,
430 conn->internal->schedule);
432 /** Cannot create UDP stream */
433 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
434 conn->callback_context);
435 silc_fsm_next(fsm, silc_client_st_connect_error);
436 return SILC_FSM_CONTINUE;
439 /* Set the new stream to packet stream */
440 old = silc_packet_stream_get_stream(conn->stream);
441 silc_packet_stream_set_stream(conn->stream, stream,
442 conn->internal->schedule);
443 silc_packet_stream_set_iv_included(conn->stream);
444 silc_packet_set_sid(conn->stream, 0);
446 /* Delete the old stream */
447 silc_stream_destroy(old);
449 /** Start authentication */
450 silc_fsm_next(fsm, silc_client_st_connect_auth);
451 return SILC_FSM_CONTINUE;
454 /* Get authentication method to be used in authentication protocol */
456 SILC_FSM_STATE(silc_client_st_connect_auth)
458 SilcClientConnection conn = fsm_context;
459 SilcClient client = conn->client;
461 SILC_LOG_DEBUG(("Get authentication data"));
463 silc_fsm_next(fsm, silc_client_st_connect_auth_start);
465 /* If authentication data not provided, ask from application */
466 if (!conn->internal->params.auth_set)
467 SILC_FSM_CALL(client->internal->ops->get_auth_method(
471 silc_client_connect_auth_method, fsm));
473 if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
474 conn->internal->params.auth = conn->private_key;
476 /* We have authentication data */
477 return SILC_FSM_CONTINUE;
480 /* Start connection authentication with remote host */
482 SILC_FSM_STATE(silc_client_st_connect_auth_start)
484 SilcClientConnection conn = fsm_context;
485 SilcClient client = conn->client;
486 SilcConnAuth connauth;
488 SILC_LOG_DEBUG(("Starting connection authentication protocol"));
490 /* Allocate connection authentication protocol */
491 connauth = silc_connauth_alloc(conn->internal->schedule,
493 client->internal->params->rekey_secs);
496 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
497 conn->callback_context);
498 silc_fsm_next(fsm, silc_client_st_connect_error);
499 return SILC_FSM_CONTINUE;
502 /** Start connection authentication */
503 silc_fsm_next(fsm, silc_client_st_connected);
504 SILC_FSM_CALL(conn->internal->op = silc_connauth_initiator(
505 connauth, SILC_CONN_CLIENT,
506 conn->internal->params.auth_method,
507 conn->internal->params.auth,
508 conn->internal->params.auth_len,
509 silc_client_connect_auth_completion,
513 /* Connection fully established */
515 SILC_FSM_STATE(silc_client_st_connected)
517 SilcClientConnection conn = fsm_context;
518 SilcClient client = conn->client;
520 SILC_LOG_DEBUG(("Connection established"));
522 /* If we connected to server, now register to network. */
523 if (conn->type == SILC_CONN_SERVER &&
524 !conn->internal->params.no_authentication) {
526 /* If detach data is provided, resume the session. */
527 if (conn->internal->params.detach_data &&
528 conn->internal->params.detach_data_len) {
529 /** Resume detached session */
530 silc_fsm_next(fsm, silc_client_st_resume);
532 /** Register to network */
533 silc_fsm_next(fsm, silc_client_st_register);
536 return SILC_FSM_CONTINUE;
539 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
541 /* Call connection callback */
542 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
543 conn->callback_context);
545 return SILC_FSM_FINISH;
548 /* Error during connecting */
550 SILC_FSM_STATE(silc_client_st_connect_error)
552 SilcClientConnection conn = fsm_context;
554 /* Signal to close connection */
555 if (!conn->internal->disconnected) {
556 conn->internal->disconnected = TRUE;
557 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
560 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
562 return SILC_FSM_FINISH;