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;
34 /************************ Static utility functions **************************/
36 /* Callback called after connected to remote host */
38 static void silc_client_connect_callback(SilcNetStatus status,
39 SilcStream stream, void *context)
41 SilcFSMThread fsm = context;
42 SilcClientConnection conn = silc_fsm_get_context(fsm);
43 SilcClient client = conn->client;
45 if (conn->internal->verbose) {
49 case SILC_NET_UNKNOWN_IP:
50 client->internal->ops->say(
51 client, conn, SILC_CLIENT_MESSAGE_ERROR,
52 "Could not connect to host %s: unknown IP address",
55 case SILC_NET_UNKNOWN_HOST:
56 client->internal->ops->say(
57 client, conn, SILC_CLIENT_MESSAGE_ERROR,
58 "Could not connect to host %s: unknown host name",
61 case SILC_NET_HOST_UNREACHABLE:
62 client->internal->ops->say(
63 client, conn, SILC_CLIENT_MESSAGE_ERROR,
64 "Could not connect to host %s: network unreachable",
67 case SILC_NET_CONNECTION_REFUSED:
68 client->internal->ops->say(
69 client, conn, SILC_CLIENT_MESSAGE_ERROR,
70 "Could not connect to host %s: connection refused",
73 case SILC_NET_CONNECTION_TIMEOUT:
74 client->internal->ops->say(
75 client, conn, SILC_CLIENT_MESSAGE_ERROR,
76 "Could not connect to host %s: connection timeout",
80 client->internal->ops->say(
81 client, conn, SILC_CLIENT_MESSAGE_ERROR,
82 "Could not connect to host %s",
88 if (status != SILC_NET_OK) {
89 /* Notify application of failure */
90 SILC_LOG_DEBUG(("Connecting failed"));
91 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
92 silc_fsm_next(fsm, silc_client_st_connect_error);
93 SILC_FSM_CALL_CONTINUE(fsm);
97 SILC_LOG_DEBUG(("Connected"));
99 /* Create packet stream */
100 conn->stream = silc_packet_stream_create(client->internal->packet_engine,
101 conn->internal->schedule, stream);
103 SILC_LOG_DEBUG(("Could not create packet stream"));
104 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
105 silc_fsm_next(fsm, silc_client_st_connect_error);
106 SILC_FSM_CALL_CONTINUE(fsm);
110 silc_packet_set_context(conn->stream, conn);
112 /* Connection created successfully */
113 SILC_FSM_CALL_CONTINUE(fsm);
116 /* Called after application has verified remote host's public key */
118 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
120 VerifyKeyContext verify = (VerifyKeyContext)context;
122 SILC_LOG_DEBUG(("Start"));
124 /* Call the completion callback back to the SKE */
125 verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
126 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
127 verify->completion_context);
132 /* Verify remote host's public key */
134 static void silc_client_ke_verify_key(SilcSKE ske,
135 SilcPublicKey public_key,
137 SilcSKEVerifyCbCompletion completion,
138 void *completion_context)
140 SilcFSMThread fsm = context;
141 SilcClientConnection conn = silc_fsm_get_context(fsm);
142 SilcClient client = conn->client;
143 VerifyKeyContext verify;
145 /* If we provided repository for SKE and we got here the key was not
146 found from the repository. */
147 if (conn->internal->params.repository &&
148 !conn->internal->params.verify_notfound) {
149 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
154 SILC_LOG_DEBUG(("Verify remote public key"));
156 verify = silc_calloc(1, sizeof(*verify));
158 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
163 verify->completion = completion;
164 verify->completion_context = completion_context;
166 /* Verify public key in application */
167 client->internal->ops->verify_public_key(client, conn,
168 conn->type, public_key,
169 silc_client_ke_verify_key_cb,
173 /* Key exchange protocol completion callback */
175 static void silc_client_ke_completion(SilcSKE ske,
176 SilcSKEStatus status,
177 SilcSKESecurityProperties prop,
178 SilcSKEKeyMaterial keymat,
179 SilcSKERekeyMaterial rekey,
182 SilcFSMThread fsm = context;
183 SilcClientConnection conn = silc_fsm_get_context(fsm);
184 SilcClient client = conn->client;
185 SilcCipher send_key, receive_key;
186 SilcHmac hmac_send, hmac_receive;
188 if (status != SILC_SKE_STATUS_OK) {
189 /* Key exchange failed */
190 SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
191 conn->remote_host, silc_ske_map_status(status), status));
193 if (conn->internal->verbose)
194 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
195 "Error during key exchange with %s: %s",
197 silc_ske_map_status(status));
199 silc_fsm_next(fsm, silc_client_st_connect_error);
200 SILC_FSM_CALL_CONTINUE(fsm);
204 SILC_LOG_DEBUG(("Setting keys into use"));
206 /* Set the keys into use. Data will be encrypted after this. */
207 if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
208 &hmac_send, &hmac_receive, &conn->internal->hash)) {
209 /* Error setting keys */
210 SILC_LOG_DEBUG(("Could not set keys into use"));
212 if (conn->internal->verbose)
213 client->internal->ops->say(
214 client, conn, SILC_CLIENT_MESSAGE_ERROR,
215 "Error during key exchange with %s: cannot use keys",
218 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, conn->context);
220 silc_fsm_next(fsm, silc_client_st_connect_error);
221 SILC_FSM_CALL_CONTINUE(fsm);
225 silc_packet_set_ciphers(conn->stream, send_key, receive_key);
226 silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
228 conn->internal->rekey = rekey;
230 /* Key exchange done */
231 SILC_FSM_CALL_CONTINUE(fsm);
234 /* Callback called by application to return authentication data */
236 static void silc_client_connect_auth_method(SilcBool success,
237 SilcAuthMethod auth_meth,
238 void *auth, SilcUInt32 auth_len,
241 SilcFSMThread fsm = context;
242 SilcClientConnection conn = silc_fsm_get_context(fsm);
244 conn->internal->params.auth_method = SILC_AUTH_NONE;
247 conn->internal->params.auth_method = auth_meth;
248 conn->internal->params.auth = auth;
249 conn->internal->params.auth_len = auth_len;
252 SILC_FSM_CALL_CONTINUE(fsm);
255 /* Connection authentication completion callback */
257 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
261 SilcFSMThread fsm = context;
262 SilcClientConnection conn = silc_fsm_get_context(fsm);
263 SilcClient client = conn->client;
265 silc_connauth_free(connauth);
268 if (conn->internal->verbose)
269 client->internal->ops->say(
270 client, conn, SILC_CLIENT_MESSAGE_ERROR,
271 "Authentication failed");
273 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, conn->context);
274 silc_fsm_next(fsm, silc_client_st_connect_error);
277 SILC_FSM_CALL_CONTINUE(fsm);
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,
292 silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
294 if (conn->internal->params.udp) {
297 if (!conn->internal->params.local_ip) {
298 /** IP address not given */
299 SILC_LOG_ERROR(("Local UDP IP address not specified"));
300 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
301 silc_fsm_next(fsm, silc_client_st_connect_error);
302 return SILC_FSM_CONTINUE;
306 stream = silc_net_udp_connect(conn->internal->params.local_ip,
307 conn->internal->params.local_port,
308 conn->remote_host, conn->remote_port,
309 conn->internal->schedule);
311 SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
312 SILC_NET_HOST_UNREACHABLE,
316 SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host,
318 conn->internal->schedule,
319 silc_client_connect_callback, fsm));
323 /* Starts key exchange protocol with remote host */
325 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
327 SilcClientConnection conn = fsm_context;
328 SilcClient client = conn->client;
329 SilcSKEParamsStruct params;
331 SILC_LOG_DEBUG(("Starting key exchange protocol"));
334 conn->internal->ske =
335 silc_ske_alloc(client->rng, conn->internal->schedule,
336 conn->internal->params.repository,
337 conn->public_key, conn->private_key, fsm);
338 if (!conn->internal->ske) {
340 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, conn->context);
341 silc_fsm_next(fsm, silc_client_st_connect_error);
342 return SILC_FSM_CONTINUE;
345 /* Set SKE callbacks */
346 silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
347 silc_client_ke_completion, fsm);
349 /* Set up key exchange parameters */
350 params.version = client->internal->silc_client_version;
351 params.flags = SILC_SKE_SP_FLAG_MUTUAL;
352 if (conn->internal->params.pfs)
353 params.flags |= SILC_SKE_SP_FLAG_PFS;
354 if (conn->internal->params.udp) {
355 params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
356 params.session_port = conn->internal->params.local_port;
359 /** Start key exchange */
360 if (conn->internal->params.no_authentication)
361 silc_fsm_next(fsm, silc_client_st_connected);
362 else if (conn->internal->params.udp)
363 silc_fsm_next(fsm, silc_client_st_connect_setup_udp);
365 silc_fsm_next(fsm, silc_client_st_connect_auth);
367 SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream,
371 /* For UDP/IP connections, set up the UDP session after successful key
374 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
376 SilcClientConnection conn = fsm_context;
377 SilcClient client = conn->client;
378 SilcStream stream, old;
379 SilcSKESecurityProperties prop;
381 SILC_LOG_DEBUG(("Setup UDP SILC session"));
383 /* Create new UDP stream */
384 prop = silc_ske_get_security_properties(conn->internal->ske);
385 stream = silc_net_udp_connect(conn->internal->params.local_ip,
386 conn->internal->params.local_port,
387 conn->remote_host, prop->remote_port,
388 conn->internal->schedule);
390 /** Cannot create UDP stream */
391 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
392 silc_fsm_next(fsm, silc_client_st_connect_error);
393 return SILC_FSM_CONTINUE;
396 /* Set the new stream to packet stream */
397 old = silc_packet_stream_get_stream(conn->stream);
398 silc_packet_stream_set_stream(conn->stream, stream,
399 conn->internal->schedule);
400 silc_packet_stream_set_iv_included(conn->stream);
401 silc_packet_set_sid(conn->stream, 0);
403 /* Delete the old stream */
404 silc_stream_destroy(old);
406 /** Start authentication */
407 silc_fsm_next(fsm, silc_client_st_connect_auth);
408 return SILC_FSM_CONTINUE;
411 /* Get authentication method to be used in authentication protocol */
413 SILC_FSM_STATE(silc_client_st_connect_auth)
415 SilcClientConnection conn = fsm_context;
416 SilcClient client = conn->client;
418 SILC_LOG_DEBUG(("Get authentication data"));
420 silc_fsm_next(fsm, silc_client_st_connect_auth_start);
422 /* If authentication data not provided, ask from application */
423 if (!conn->internal->params.auth_set)
424 SILC_FSM_CALL(client->internal->ops->
425 get_auth_method(client, conn,
426 conn->remote_host, conn->remote_port,
427 silc_client_connect_auth_method, fsm));
429 if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
430 conn->internal->params.auth = conn->private_key;
432 /* We have authentication data */
433 return SILC_FSM_CONTINUE;
436 /* Start connection authentication with remote host */
438 SILC_FSM_STATE(silc_client_st_connect_auth_start)
440 SilcClientConnection conn = fsm_context;
441 SilcClient client = conn->client;
442 SilcConnAuth connauth;
444 SILC_LOG_DEBUG(("Starting connection authentication protocol"));
446 /* Allocate connection authentication protocol */
447 connauth = silc_connauth_alloc(conn->internal->schedule,
449 client->internal->params->rekey_secs);
452 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, conn->context);
453 silc_fsm_next(fsm, silc_client_st_connect_error);
454 return SILC_FSM_CONTINUE;
457 /** Start connection authentication */
458 silc_fsm_next(fsm, silc_client_st_connected);
459 SILC_FSM_CALL(silc_connauth_initiator(connauth, SILC_CONN_CLIENT,
460 conn->internal->params.auth_method,
461 conn->internal->params.auth,
462 conn->internal->params.auth_len,
463 silc_client_connect_auth_completion,
467 /* Connection fully established */
469 SILC_FSM_STATE(silc_client_st_connected)
471 SilcClientConnection conn = fsm_context;
472 SilcClient client = conn->client;
474 SILC_LOG_DEBUG(("Connection established"));
476 /* If we connected to server, now register to network. */
477 if (conn->type == SILC_CONN_SERVER &&
478 !conn->internal->params.no_authentication) {
480 /* If detach data is provided, resume the session. */
481 if (conn->internal->params.detach_data &&
482 conn->internal->params.detach_data_len) {
483 /** Resume detached session */
484 silc_fsm_next(fsm, silc_client_st_resume);
486 /** Register to network */
487 silc_fsm_next(fsm, silc_client_st_register);
490 return SILC_FSM_CONTINUE;
493 /* Call connection callback */
494 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, conn->context);
496 return SILC_FSM_FINISH;
499 /* Error during connecting */
501 SILC_FSM_STATE(silc_client_st_connect_error)
505 /* Close connection */
507 return SILC_FSM_FINISH;