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 /* Public key verification context */
29 SilcSKEVerifyCbCompletion completion;
30 void *completion_context;
33 /************************ Static utility functions **************************/
35 /* Callback called after connected to remote host */
37 static void silc_client_connect_callback(SilcNetStatus status,
38 SilcStream stream, void *context)
40 SilcFSMThread fsm = context;
41 SilcClientConnection conn = silc_fsm_get_context(fsm);
42 SilcClient client = conn->client;
44 if (conn->internal->verbose) {
48 case SILC_NET_UNKNOWN_IP:
49 client->internal->ops->say(
50 client, conn, SILC_CLIENT_MESSAGE_ERROR,
51 "Could not connect to host %s: unknown IP address",
54 case SILC_NET_UNKNOWN_HOST:
55 client->internal->ops->say(
56 client, conn, SILC_CLIENT_MESSAGE_ERROR,
57 "Could not connect to host %s: unknown host name",
60 case SILC_NET_HOST_UNREACHABLE:
61 client->internal->ops->say(
62 client, conn, SILC_CLIENT_MESSAGE_ERROR,
63 "Could not connect to host %s: network unreachable",
66 case SILC_NET_CONNECTION_REFUSED:
67 client->internal->ops->say(
68 client, conn, SILC_CLIENT_MESSAGE_ERROR,
69 "Could not connect to host %s: connection refused",
72 case SILC_NET_CONNECTION_TIMEOUT:
73 client->internal->ops->say(
74 client, conn, SILC_CLIENT_MESSAGE_ERROR,
75 "Could not connect to host %s: connection timeout",
79 client->internal->ops->say(
80 client, conn, SILC_CLIENT_MESSAGE_ERROR,
81 "Could not connect to host %s",
87 if (status != SILC_NET_OK) {
88 /* Notify application of failure */
89 SILC_LOG_DEBUG(("Connecting failed"));
90 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0,
91 NULL, conn->callback_context);
92 silc_fsm_next(fsm, silc_client_st_connect_error);
93 SILC_FSM_CALL_CONTINUE(fsm);
97 /* Connection created successfully */
98 SILC_LOG_DEBUG(("Connected"));
99 conn->stream = (void *)stream;
100 SILC_FSM_CALL_CONTINUE(fsm);
103 /* Called after application has verified remote host's public key */
105 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
107 VerifyKeyContext verify = (VerifyKeyContext)context;
109 SILC_LOG_DEBUG(("Start"));
111 /* Call the completion callback back to the SKE */
112 verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
113 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
114 verify->completion_context);
119 /* Verify remote host's public key */
121 static void silc_client_ke_verify_key(SilcSKE ske,
122 SilcPublicKey public_key,
124 SilcSKEVerifyCbCompletion completion,
125 void *completion_context)
127 SilcFSMThread fsm = context;
128 SilcClientConnection conn = silc_fsm_get_context(fsm);
129 SilcClient client = conn->client;
130 VerifyKeyContext verify;
132 /* If we provided repository for SKE and we got here the key was not
133 found from the repository. */
134 if (conn->internal->params.repository &&
135 !conn->internal->params.verify_notfound) {
136 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
141 SILC_LOG_DEBUG(("Verify remote public key"));
143 verify = silc_calloc(1, sizeof(*verify));
145 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
150 verify->completion = completion;
151 verify->completion_context = completion_context;
153 /* Verify public key in application */
154 client->internal->ops->verify_public_key(client, conn,
155 conn->type, public_key,
156 silc_client_ke_verify_key_cb,
160 /* Key exchange protocol completion callback */
162 static void silc_client_ke_completion(SilcSKE ske,
163 SilcSKEStatus status,
164 SilcSKESecurityProperties prop,
165 SilcSKEKeyMaterial keymat,
166 SilcSKERekeyMaterial rekey,
169 SilcFSMThread fsm = context;
170 SilcClientConnection conn = silc_fsm_get_context(fsm);
171 SilcClient client = conn->client;
172 SilcCipher send_key, receive_key;
173 SilcHmac hmac_send, hmac_receive;
175 if (status != SILC_SKE_STATUS_OK) {
176 /* Key exchange failed */
177 SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
178 conn->remote_host, silc_ske_map_status(status), status));
180 if (conn->internal->verbose)
181 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
182 "Error during key exchange with %s: %s",
184 silc_ske_map_status(status));
186 silc_fsm_next(fsm, silc_client_st_connect_error);
187 SILC_FSM_CALL_CONTINUE(fsm);
191 SILC_LOG_DEBUG(("Setting keys into use"));
193 /* Set the keys into use. Data will be encrypted after this. */
194 if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
195 &hmac_send, &hmac_receive, &conn->internal->hash)) {
196 /* Error setting keys */
197 SILC_LOG_DEBUG(("Could not set keys into use"));
199 if (conn->internal->verbose)
200 client->internal->ops->say(
201 client, conn, SILC_CLIENT_MESSAGE_ERROR,
202 "Error during key exchange with %s: cannot use keys",
205 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
206 conn->callback_context);
208 silc_fsm_next(fsm, silc_client_st_connect_error);
209 SILC_FSM_CALL_CONTINUE(fsm);
213 silc_packet_set_ciphers(conn->stream, send_key, receive_key);
214 silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
216 conn->internal->rekey = rekey;
218 /* Key exchange done */
219 SILC_FSM_CALL_CONTINUE(fsm);
222 /* Callback called by application to return authentication data */
224 static void silc_client_connect_auth_method(SilcBool success,
225 SilcAuthMethod auth_meth,
226 void *auth, SilcUInt32 auth_len,
229 SilcFSMThread fsm = context;
230 SilcClientConnection conn = silc_fsm_get_context(fsm);
232 conn->internal->params.auth_method = SILC_AUTH_NONE;
235 conn->internal->params.auth_method = auth_meth;
236 conn->internal->params.auth = auth;
237 conn->internal->params.auth_len = auth_len;
240 SILC_FSM_CALL_CONTINUE(fsm);
243 /* Connection authentication completion callback */
245 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
249 SilcFSMThread fsm = context;
250 SilcClientConnection conn = silc_fsm_get_context(fsm);
251 SilcClient client = conn->client;
253 silc_connauth_free(connauth);
256 if (conn->internal->verbose)
257 client->internal->ops->say(
258 client, conn, SILC_CLIENT_MESSAGE_ERROR,
259 "Authentication failed");
261 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
262 conn->callback_context);
263 silc_fsm_next(fsm, silc_client_st_connect_error);
266 SILC_FSM_CALL_CONTINUE(fsm);
269 /*************************** Connect remote host ****************************/
271 /* Creates a connection to remote host */
273 SILC_FSM_STATE(silc_client_st_connect)
275 SilcClientConnection conn = fsm_context;
276 SilcClient client = conn->client;
278 SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
282 silc_fsm_next(fsm, silc_client_st_connect_set_stream);
284 if (conn->internal->params.udp) {
287 if (!conn->internal->params.local_ip) {
288 /** IP address not given */
289 SILC_LOG_ERROR(("Local UDP IP address not specified"));
290 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
291 conn->callback_context);
292 silc_fsm_next(fsm, silc_client_st_connect_error);
293 return SILC_FSM_CONTINUE;
297 stream = silc_net_udp_connect(conn->internal->params.local_ip,
298 conn->internal->params.local_port,
299 conn->remote_host, conn->remote_port,
300 conn->internal->schedule);
302 SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
303 SILC_NET_HOST_UNREACHABLE,
307 SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect(
308 NULL, conn->remote_host,
310 conn->internal->schedule,
311 silc_client_connect_callback, fsm));
315 /* Sets the new connection stream into use and creates packet stream */
317 SILC_FSM_STATE(silc_client_st_connect_set_stream)
319 SilcClientConnection conn = fsm_context;
320 SilcClient client = conn->client;
322 /* Create packet stream */
323 conn->stream = silc_packet_stream_create(client->internal->packet_engine,
324 conn->internal->schedule,
325 (SilcStream)conn->stream);
327 /** Cannot create packet stream */
328 SILC_LOG_DEBUG(("Could not create packet stream"));
329 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
330 conn->callback_context);
331 silc_fsm_next(fsm, silc_client_st_connect_error);
332 return SILC_FSM_CONTINUE;
335 silc_packet_set_context(conn->stream, conn);
337 /** Start key exchange */
338 silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
339 return SILC_FSM_CONTINUE;
342 /* Starts key exchange protocol with remote host */
344 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
346 SilcClientConnection conn = fsm_context;
347 SilcClient client = conn->client;
348 SilcSKEParamsStruct params;
350 SILC_LOG_DEBUG(("Starting key exchange protocol"));
353 conn->internal->ske =
354 silc_ske_alloc(client->rng, conn->internal->schedule,
355 conn->internal->params.repository,
356 conn->public_key, conn->private_key, fsm);
357 if (!conn->internal->ske) {
359 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
360 conn->callback_context);
361 silc_fsm_next(fsm, silc_client_st_connect_error);
362 return SILC_FSM_CONTINUE;
365 /* Set SKE callbacks */
366 silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
367 silc_client_ke_completion, fsm);
369 /* Set up key exchange parameters */
370 params.version = client->internal->silc_client_version;
371 params.flags = SILC_SKE_SP_FLAG_MUTUAL;
372 if (conn->internal->params.pfs)
373 params.flags |= SILC_SKE_SP_FLAG_PFS;
374 if (conn->internal->params.udp) {
375 params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
376 params.session_port = conn->internal->params.local_port;
379 if (conn->internal->params.no_authentication)
380 /** Run key exchange (no auth) */
381 silc_fsm_next(fsm, silc_client_st_connected);
382 else if (conn->internal->params.udp)
383 /** Run key exchange (UDP)*/
384 silc_fsm_next(fsm, silc_client_st_connect_setup_udp);
386 /** Run key exchange (TCP) */
387 silc_fsm_next(fsm, silc_client_st_connect_auth);
389 SILC_FSM_CALL(conn->internal->op = silc_ske_initiator(conn->internal->ske,
394 /* For UDP/IP connections, set up the UDP session after successful key
397 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
399 SilcClientConnection conn = fsm_context;
400 SilcClient client = conn->client;
401 SilcStream stream, old;
402 SilcSKESecurityProperties prop;
404 SILC_LOG_DEBUG(("Setup UDP SILC session"));
406 /* Create new UDP stream */
407 prop = silc_ske_get_security_properties(conn->internal->ske);
408 stream = silc_net_udp_connect(conn->internal->params.local_ip,
409 conn->internal->params.local_port,
410 conn->remote_host, prop->remote_port,
411 conn->internal->schedule);
413 /** Cannot create UDP stream */
414 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
415 conn->callback_context);
416 silc_fsm_next(fsm, silc_client_st_connect_error);
417 return SILC_FSM_CONTINUE;
420 /* Set the new stream to packet stream */
421 old = silc_packet_stream_get_stream(conn->stream);
422 silc_packet_stream_set_stream(conn->stream, stream,
423 conn->internal->schedule);
424 silc_packet_stream_set_iv_included(conn->stream);
425 silc_packet_set_sid(conn->stream, 0);
427 /* Delete the old stream */
428 silc_stream_destroy(old);
430 /** Start authentication */
431 silc_fsm_next(fsm, silc_client_st_connect_auth);
432 return SILC_FSM_CONTINUE;
435 /* Get authentication method to be used in authentication protocol */
437 SILC_FSM_STATE(silc_client_st_connect_auth)
439 SilcClientConnection conn = fsm_context;
440 SilcClient client = conn->client;
442 SILC_LOG_DEBUG(("Get authentication data"));
444 silc_fsm_next(fsm, silc_client_st_connect_auth_start);
446 /* If authentication data not provided, ask from application */
447 if (!conn->internal->params.auth_set)
448 SILC_FSM_CALL(client->internal->ops->get_auth_method(
452 silc_client_connect_auth_method, fsm));
454 if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
455 conn->internal->params.auth = conn->private_key;
457 /* We have authentication data */
458 return SILC_FSM_CONTINUE;
461 /* Start connection authentication with remote host */
463 SILC_FSM_STATE(silc_client_st_connect_auth_start)
465 SilcClientConnection conn = fsm_context;
466 SilcClient client = conn->client;
467 SilcConnAuth connauth;
469 SILC_LOG_DEBUG(("Starting connection authentication protocol"));
471 /* Allocate connection authentication protocol */
472 connauth = silc_connauth_alloc(conn->internal->schedule,
474 client->internal->params->rekey_secs);
477 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
478 conn->callback_context);
479 silc_fsm_next(fsm, silc_client_st_connect_error);
480 return SILC_FSM_CONTINUE;
483 /** Start connection authentication */
484 silc_fsm_next(fsm, silc_client_st_connected);
485 SILC_FSM_CALL(conn->internal->op = silc_connauth_initiator(
486 connauth, SILC_CONN_CLIENT,
487 conn->internal->params.auth_method,
488 conn->internal->params.auth,
489 conn->internal->params.auth_len,
490 silc_client_connect_auth_completion,
494 /* Connection fully established */
496 SILC_FSM_STATE(silc_client_st_connected)
498 SilcClientConnection conn = fsm_context;
499 SilcClient client = conn->client;
501 SILC_LOG_DEBUG(("Connection established"));
503 /* If we connected to server, now register to network. */
504 if (conn->type == SILC_CONN_SERVER &&
505 !conn->internal->params.no_authentication) {
507 /* If detach data is provided, resume the session. */
508 if (conn->internal->params.detach_data &&
509 conn->internal->params.detach_data_len) {
510 /** Resume detached session */
511 silc_fsm_next(fsm, silc_client_st_resume);
513 /** Register to network */
514 silc_fsm_next(fsm, silc_client_st_register);
517 return SILC_FSM_CONTINUE;
520 /* Call connection callback */
521 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
522 conn->callback_context);
524 return SILC_FSM_FINISH;
527 /* Error during connecting */
529 SILC_FSM_STATE(silc_client_st_connect_error)
531 SilcClientConnection conn = fsm_context;
533 /* Signal to close connection */
534 conn->internal->disconnected = TRUE;
535 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
537 return SILC_FSM_FINISH;