3b656229e65fcd605a7212c658314d9e089f304c
[silc.git] / lib / silcclient / client_connect.c
1 /*
2
3   client_st_connect.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2006 Pekka Riikonen
8
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.
12
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.
17
18 */
19
20 #include "silc.h"
21 #include "silcclient.h"
22 #include "client_internal.h"
23
24 /************************** Types and definitions ***************************/
25
26 /* Public key verification context */
27 typedef struct {
28   SilcSKE ske;
29   SilcSKEVerifyCbCompletion completion;
30   void *completion_context;
31 } *VerifyKeyContext;
32
33
34 /************************ Static utility functions **************************/
35
36 /* Callback called after connected to remote host */
37
38 static void silc_client_connect_callback(SilcNetStatus status,
39                                          SilcStream stream, void *context)
40 {
41   SilcFSMThread fsm = context;
42   SilcClientConnection conn = silc_fsm_get_context(fsm);
43   SilcClient client = conn->client;
44
45   if (conn->internal->verbose) {
46     switch (status) {
47     case SILC_NET_OK:
48       break;
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",
53                    conn->remote_host);
54       break;
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",
59                    conn->remote_host);
60       break;
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",
65                    conn->remote_host);
66       break;
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",
71                    conn->remote_host);
72       break;
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",
77                    conn->remote_host);
78       break;
79     default:
80       client->internal->ops->say(
81                    client, conn, SILC_CLIENT_MESSAGE_ERROR,
82                    "Could not connect to host %s",
83                    conn->remote_host);
84       break;
85     }
86   }
87
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, 0,
92                    NULL, conn->callback_context);
93     silc_fsm_next(fsm, silc_client_st_connect_error);
94     SILC_FSM_CALL_CONTINUE(fsm);
95     return;
96   }
97
98   /* Connection created successfully */
99   SILC_LOG_DEBUG(("Connected"));
100   conn->stream = (void *)stream;
101   SILC_FSM_CALL_CONTINUE(fsm);
102 }
103
104 /* Called after application has verified remote host's public key */
105
106 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
107 {
108   VerifyKeyContext verify = (VerifyKeyContext)context;
109
110   SILC_LOG_DEBUG(("Start"));
111
112   /* Call the completion callback back to the SKE */
113   verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
114                      SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
115                      verify->completion_context);
116
117   silc_free(verify);
118 }
119
120 /* Verify remote host's public key */
121
122 static void silc_client_ke_verify_key(SilcSKE ske,
123                                       SilcPublicKey public_key,
124                                       void *context,
125                                       SilcSKEVerifyCbCompletion completion,
126                                       void *completion_context)
127 {
128   SilcFSMThread fsm = context;
129   SilcClientConnection conn = silc_fsm_get_context(fsm);
130   SilcClient client = conn->client;
131   VerifyKeyContext verify;
132
133   /* If we provided repository for SKE and we got here the key was not
134      found from the repository. */
135   if (conn->internal->params.repository &&
136       !conn->internal->params.verify_notfound) {
137     completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
138                completion_context);
139     return;
140   }
141
142   SILC_LOG_DEBUG(("Verify remote public key"));
143
144   verify = silc_calloc(1, sizeof(*verify));
145   if (!verify) {
146     completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
147                completion_context);
148     return;
149   }
150   verify->ske = ske;
151   verify->completion = completion;
152   verify->completion_context = completion_context;
153
154   /* Verify public key in application */
155   client->internal->ops->verify_public_key(client, conn,
156                                            conn->type, public_key,
157                                            silc_client_ke_verify_key_cb,
158                                            verify);
159 }
160
161 /* Key exchange protocol completion callback */
162
163 static void silc_client_ke_completion(SilcSKE ske,
164                                       SilcSKEStatus status,
165                                       SilcSKESecurityProperties prop,
166                                       SilcSKEKeyMaterial keymat,
167                                       SilcSKERekeyMaterial rekey,
168                                       void *context)
169 {
170   SilcFSMThread fsm = context;
171   SilcClientConnection conn = silc_fsm_get_context(fsm);
172   SilcClient client = conn->client;
173   SilcCipher send_key, receive_key;
174   SilcHmac hmac_send, hmac_receive;
175
176   if (status != SILC_SKE_STATUS_OK) {
177     /* Key exchange failed */
178     SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
179                     conn->remote_host, silc_ske_map_status(status), status));
180
181     if (conn->internal->verbose)
182       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
183                                  "Error during key exchange with %s: %s",
184                                  conn->remote_host,
185                                  silc_ske_map_status(status));
186
187     silc_fsm_next(fsm, silc_client_st_connect_error);
188     SILC_FSM_CALL_CONTINUE(fsm);
189     return;
190   }
191
192   SILC_LOG_DEBUG(("Setting keys into use"));
193
194   /* Set the keys into use.  Data will be encrypted after this. */
195   if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
196                          &hmac_send, &hmac_receive, &conn->internal->hash)) {
197     /* Error setting keys */
198     SILC_LOG_DEBUG(("Could not set keys into use"));
199
200     if (conn->internal->verbose)
201       client->internal->ops->say(
202                        client, conn, SILC_CLIENT_MESSAGE_ERROR,
203                        "Error during key exchange with %s: cannot use keys",
204                        conn->remote_host);
205
206     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
207                    conn->callback_context);
208
209     silc_fsm_next(fsm, silc_client_st_connect_error);
210     SILC_FSM_CALL_CONTINUE(fsm);
211     return;
212   }
213
214   silc_packet_set_ciphers(conn->stream, send_key, receive_key);
215   silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
216
217   conn->internal->rekey = rekey;
218
219   /* Key exchange done */
220   SILC_FSM_CALL_CONTINUE(fsm);
221 }
222
223 /* Callback called by application to return authentication data */
224
225 static void silc_client_connect_auth_method(SilcBool success,
226                                             SilcAuthMethod auth_meth,
227                                             void *auth, SilcUInt32 auth_len,
228                                             void *context)
229 {
230   SilcFSMThread fsm = context;
231   SilcClientConnection conn = silc_fsm_get_context(fsm);
232
233   conn->internal->params.auth_method = SILC_AUTH_NONE;
234
235   if (success) {
236     conn->internal->params.auth_method = auth_meth;
237     conn->internal->params.auth = auth;
238     conn->internal->params.auth_len = auth_len;
239   }
240
241   SILC_FSM_CALL_CONTINUE(fsm);
242 }
243
244 /* Connection authentication completion callback */
245
246 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
247                                                 SilcBool success,
248                                                 void *context)
249 {
250   SilcFSMThread fsm = context;
251   SilcClientConnection conn = silc_fsm_get_context(fsm);
252   SilcClient client = conn->client;
253
254   silc_connauth_free(connauth);
255
256   if (!success) {
257     if (conn->internal->verbose)
258         client->internal->ops->say(
259                         client, conn, SILC_CLIENT_MESSAGE_ERROR,
260                         "Authentication failed");
261
262     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
263                    conn->callback_context);
264     silc_fsm_next(fsm, silc_client_st_connect_error);
265   }
266
267   SILC_FSM_CALL_CONTINUE(fsm);
268 }
269
270 /*************************** Connect remote host ****************************/
271
272 /* Creates a connection to remote host */
273
274 SILC_FSM_STATE(silc_client_st_connect)
275 {
276   SilcClientConnection conn = fsm_context;
277   SilcClient client = conn->client;
278
279   SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
280                   conn->remote_port));
281
282   /** Connect */
283   silc_fsm_next(fsm, silc_client_st_connect_set_stream);
284
285   if (conn->internal->params.udp) {
286     SilcStream stream;
287
288     if (!conn->internal->params.local_ip) {
289       /** IP address not given */
290       SILC_LOG_ERROR(("Local UDP IP address not specified"));
291       conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
292                      conn->callback_context);
293       silc_fsm_next(fsm, silc_client_st_connect_error);
294       return SILC_FSM_CONTINUE;
295     }
296
297     /* Connect (UDP) */
298     stream = silc_net_udp_connect(conn->internal->params.local_ip,
299                                   conn->internal->params.local_port,
300                                   conn->remote_host, conn->remote_port,
301                                   conn->internal->schedule);
302
303     SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
304                                                SILC_NET_HOST_UNREACHABLE,
305                                                stream, fsm));
306   } else {
307     /* Connect (TCP) */
308     SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host,
309                                        conn->remote_port,
310                                        conn->internal->schedule,
311                                        silc_client_connect_callback, fsm));
312   }
313 }
314
315 /* Sets the new connection stream into use and creates packet stream */
316
317 SILC_FSM_STATE(silc_client_st_connect_set_stream)
318 {
319   SilcClientConnection conn = fsm_context;
320   SilcClient client = conn->client;
321
322   /* Create packet stream */
323   conn->stream = silc_packet_stream_create(client->internal->packet_engine,
324                                            conn->internal->schedule,
325                                            (SilcStream)conn->stream);
326   if (!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;
333   }
334
335   silc_packet_set_context(conn->stream, conn);
336
337   /** Start key exchange */
338   silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
339   return SILC_FSM_CONTINUE;
340 }
341
342 /* Starts key exchange protocol with remote host */
343
344 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
345 {
346   SilcClientConnection conn = fsm_context;
347   SilcClient client = conn->client;
348   SilcSKEParamsStruct params;
349
350   SILC_LOG_DEBUG(("Starting key exchange protocol"));
351
352   /* Allocate SKE */
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) {
358     /** Out of memory */
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;
363   }
364
365   /* Set SKE callbacks */
366   silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
367                          silc_client_ke_completion, fsm);
368
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;
377   }
378
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);
385   else
386     /** Run key exchange (TCP) */
387     silc_fsm_next(fsm, silc_client_st_connect_auth);
388
389   SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream,
390                                    &params, NULL));
391 }
392
393 /* For UDP/IP connections, set up the UDP session after successful key
394    exchange protocol */
395
396 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
397 {
398   SilcClientConnection conn = fsm_context;
399   SilcClient client = conn->client;
400   SilcStream stream, old;
401   SilcSKESecurityProperties prop;
402
403   SILC_LOG_DEBUG(("Setup UDP SILC session"));
404
405   /* Create new UDP stream */
406   prop = silc_ske_get_security_properties(conn->internal->ske);
407   stream = silc_net_udp_connect(conn->internal->params.local_ip,
408                                 conn->internal->params.local_port,
409                                 conn->remote_host, prop->remote_port,
410                                 conn->internal->schedule);
411   if (!stream) {
412     /** Cannot create UDP stream */
413     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
414                    conn->callback_context);
415     silc_fsm_next(fsm, silc_client_st_connect_error);
416     return SILC_FSM_CONTINUE;
417   }
418
419   /* Set the new stream to packet stream */
420   old = silc_packet_stream_get_stream(conn->stream);
421   silc_packet_stream_set_stream(conn->stream, stream,
422                                 conn->internal->schedule);
423   silc_packet_stream_set_iv_included(conn->stream);
424   silc_packet_set_sid(conn->stream, 0);
425
426   /* Delete the old stream */
427   silc_stream_destroy(old);
428
429   /** Start authentication */
430   silc_fsm_next(fsm, silc_client_st_connect_auth);
431   return SILC_FSM_CONTINUE;
432 }
433
434 /* Get authentication method to be used in authentication protocol */
435
436 SILC_FSM_STATE(silc_client_st_connect_auth)
437 {
438   SilcClientConnection conn = fsm_context;
439   SilcClient client = conn->client;
440
441   SILC_LOG_DEBUG(("Get authentication data"));
442
443   silc_fsm_next(fsm, silc_client_st_connect_auth_start);
444
445   /* If authentication data not provided, ask from application */
446   if (!conn->internal->params.auth_set)
447     SILC_FSM_CALL(client->internal->ops->get_auth_method(
448                                     client, conn,
449                                     conn->remote_host,
450                                     conn->remote_port,
451                                     silc_client_connect_auth_method, fsm));
452
453   if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
454     conn->internal->params.auth = conn->private_key;
455
456   /* We have authentication data */
457   return SILC_FSM_CONTINUE;
458 }
459
460 /* Start connection authentication with remote host */
461
462 SILC_FSM_STATE(silc_client_st_connect_auth_start)
463 {
464   SilcClientConnection conn = fsm_context;
465   SilcClient client = conn->client;
466   SilcConnAuth connauth;
467
468   SILC_LOG_DEBUG(("Starting connection authentication protocol"));
469
470   /* Allocate connection authentication protocol */
471   connauth = silc_connauth_alloc(conn->internal->schedule,
472                                  conn->internal->ske,
473                                  client->internal->params->rekey_secs);
474   if (!connauth) {
475     /** Out of memory */
476     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
477                    conn->callback_context);
478     silc_fsm_next(fsm, silc_client_st_connect_error);
479     return SILC_FSM_CONTINUE;
480   }
481
482   /** Start connection authentication */
483   silc_fsm_next(fsm, silc_client_st_connected);
484   SILC_FSM_CALL(silc_connauth_initiator(connauth, SILC_CONN_CLIENT,
485                                         conn->internal->params.auth_method,
486                                         conn->internal->params.auth,
487                                         conn->internal->params.auth_len,
488                                         silc_client_connect_auth_completion,
489                                         fsm));
490 }
491
492 /* Connection fully established */
493
494 SILC_FSM_STATE(silc_client_st_connected)
495 {
496   SilcClientConnection conn = fsm_context;
497   SilcClient client = conn->client;
498
499   SILC_LOG_DEBUG(("Connection established"));
500
501   /* If we connected to server, now register to network. */
502   if (conn->type == SILC_CONN_SERVER &&
503       !conn->internal->params.no_authentication) {
504
505     /* If detach data is provided, resume the session. */
506     if (conn->internal->params.detach_data &&
507         conn->internal->params.detach_data_len) {
508       /** Resume detached session */
509       silc_fsm_next(fsm, silc_client_st_resume);
510     } else {
511       /** Register to network */
512       silc_fsm_next(fsm, silc_client_st_register);
513     }
514
515     return SILC_FSM_CONTINUE;
516   }
517
518   /* Call connection callback */
519   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
520                  conn->callback_context);
521
522   return SILC_FSM_FINISH;
523 }
524
525 /* Error during connecting */
526
527 SILC_FSM_STATE(silc_client_st_connect_error)
528 {
529
530   /* XXX */
531   /* Close connection */
532
533   return SILC_FSM_FINISH;
534 }