More client library rewrites.
[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 /************************ Static utility functions **************************/
34
35 /* Callback called after connected to remote host */
36
37 static void silc_client_connect_callback(SilcNetStatus status,
38                                          SilcStream stream, void *context)
39 {
40   SilcFSMThread fsm = context;
41   SilcClientConnection conn = silc_fsm_get_context(fsm);
42   SilcClient client = conn->client;
43
44   if (conn->internal->verbose) {
45     switch (status) {
46     case SILC_NET_OK:
47       break;
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",
52                    conn->remote_host);
53       break;
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",
58                    conn->remote_host);
59       break;
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",
64                    conn->remote_host);
65       break;
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",
70                    conn->remote_host);
71       break;
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",
76                    conn->remote_host);
77       break;
78     default:
79       client->internal->ops->say(
80                    client, conn, SILC_CLIENT_MESSAGE_ERROR,
81                    "Could not connect to host %s",
82                    conn->remote_host);
83       break;
84     }
85   }
86
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);
94     return;
95   }
96
97   /* Connection created successfully */
98   SILC_LOG_DEBUG(("Connected"));
99   conn->stream = (void *)stream;
100   SILC_FSM_CALL_CONTINUE(fsm);
101 }
102
103 /* Called after application has verified remote host's public key */
104
105 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
106 {
107   VerifyKeyContext verify = (VerifyKeyContext)context;
108
109   SILC_LOG_DEBUG(("Start"));
110
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);
115
116   silc_free(verify);
117 }
118
119 /* Verify remote host's public key */
120
121 static void silc_client_ke_verify_key(SilcSKE ske,
122                                       SilcPublicKey public_key,
123                                       void *context,
124                                       SilcSKEVerifyCbCompletion completion,
125                                       void *completion_context)
126 {
127   SilcFSMThread fsm = context;
128   SilcClientConnection conn = silc_fsm_get_context(fsm);
129   SilcClient client = conn->client;
130   VerifyKeyContext verify;
131
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,
137                completion_context);
138     return;
139   }
140
141   SILC_LOG_DEBUG(("Verify remote public key"));
142
143   verify = silc_calloc(1, sizeof(*verify));
144   if (!verify) {
145     completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
146                completion_context);
147     return;
148   }
149   verify->ske = ske;
150   verify->completion = completion;
151   verify->completion_context = completion_context;
152
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,
157                                            verify);
158 }
159
160 /* Key exchange protocol completion callback */
161
162 static void silc_client_ke_completion(SilcSKE ske,
163                                       SilcSKEStatus status,
164                                       SilcSKESecurityProperties prop,
165                                       SilcSKEKeyMaterial keymat,
166                                       SilcSKERekeyMaterial rekey,
167                                       void *context)
168 {
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;
174
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));
179
180     if (conn->internal->verbose)
181       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
182                                  "Error during key exchange with %s: %s",
183                                  conn->remote_host,
184                                  silc_ske_map_status(status));
185
186     silc_fsm_next(fsm, silc_client_st_connect_error);
187     SILC_FSM_CALL_CONTINUE(fsm);
188     return;
189   }
190
191   SILC_LOG_DEBUG(("Setting keys into use"));
192
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"));
198
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",
203                        conn->remote_host);
204
205     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
206                    conn->callback_context);
207
208     silc_fsm_next(fsm, silc_client_st_connect_error);
209     SILC_FSM_CALL_CONTINUE(fsm);
210     return;
211   }
212
213   silc_packet_set_ciphers(conn->stream, send_key, receive_key);
214   silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
215
216   conn->internal->rekey = rekey;
217
218   /* Key exchange done */
219   SILC_FSM_CALL_CONTINUE(fsm);
220 }
221
222 /* Callback called by application to return authentication data */
223
224 static void silc_client_connect_auth_method(SilcBool success,
225                                             SilcAuthMethod auth_meth,
226                                             void *auth, SilcUInt32 auth_len,
227                                             void *context)
228 {
229   SilcFSMThread fsm = context;
230   SilcClientConnection conn = silc_fsm_get_context(fsm);
231
232   conn->internal->params.auth_method = SILC_AUTH_NONE;
233
234   if (success) {
235     conn->internal->params.auth_method = auth_meth;
236     conn->internal->params.auth = auth;
237     conn->internal->params.auth_len = auth_len;
238   }
239
240   SILC_FSM_CALL_CONTINUE(fsm);
241 }
242
243 /* Connection authentication completion callback */
244
245 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
246                                                 SilcBool success,
247                                                 void *context)
248 {
249   SilcFSMThread fsm = context;
250   SilcClientConnection conn = silc_fsm_get_context(fsm);
251   SilcClient client = conn->client;
252
253   silc_connauth_free(connauth);
254
255   if (!success) {
256     if (conn->internal->verbose)
257         client->internal->ops->say(
258                         client, conn, SILC_CLIENT_MESSAGE_ERROR,
259                         "Authentication failed");
260
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);
264   }
265
266   SILC_FSM_CALL_CONTINUE(fsm);
267 }
268
269 /*************************** Connect remote host ****************************/
270
271 /* Creates a connection to remote host */
272
273 SILC_FSM_STATE(silc_client_st_connect)
274 {
275   SilcClientConnection conn = fsm_context;
276   SilcClient client = conn->client;
277
278   SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
279                   conn->remote_port));
280
281   /** Connect */
282   silc_fsm_next(fsm, silc_client_st_connect_set_stream);
283
284   if (conn->internal->params.udp) {
285     SilcStream stream;
286
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;
294     }
295
296     /* Connect (UDP) */
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);
301
302     SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
303                                                SILC_NET_HOST_UNREACHABLE,
304                                                stream, fsm));
305   } else {
306     /* Connect (TCP) */
307     SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect(
308                                        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(conn->internal->op = silc_ske_initiator(conn->internal->ske,
390                                                         conn->stream,
391                                                         &params, NULL));
392 }
393
394 /* For UDP/IP connections, set up the UDP session after successful key
395    exchange protocol */
396
397 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
398 {
399   SilcClientConnection conn = fsm_context;
400   SilcClient client = conn->client;
401   SilcStream stream, old;
402   SilcSKESecurityProperties prop;
403
404   SILC_LOG_DEBUG(("Setup UDP SILC session"));
405
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);
412   if (!stream) {
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;
418   }
419
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);
426
427   /* Delete the old stream */
428   silc_stream_destroy(old);
429
430   /** Start authentication */
431   silc_fsm_next(fsm, silc_client_st_connect_auth);
432   return SILC_FSM_CONTINUE;
433 }
434
435 /* Get authentication method to be used in authentication protocol */
436
437 SILC_FSM_STATE(silc_client_st_connect_auth)
438 {
439   SilcClientConnection conn = fsm_context;
440   SilcClient client = conn->client;
441
442   SILC_LOG_DEBUG(("Get authentication data"));
443
444   silc_fsm_next(fsm, silc_client_st_connect_auth_start);
445
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(
449                                     client, conn,
450                                     conn->remote_host,
451                                     conn->remote_port,
452                                     silc_client_connect_auth_method, fsm));
453
454   if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
455     conn->internal->params.auth = conn->private_key;
456
457   /* We have authentication data */
458   return SILC_FSM_CONTINUE;
459 }
460
461 /* Start connection authentication with remote host */
462
463 SILC_FSM_STATE(silc_client_st_connect_auth_start)
464 {
465   SilcClientConnection conn = fsm_context;
466   SilcClient client = conn->client;
467   SilcConnAuth connauth;
468
469   SILC_LOG_DEBUG(("Starting connection authentication protocol"));
470
471   /* Allocate connection authentication protocol */
472   connauth = silc_connauth_alloc(conn->internal->schedule,
473                                  conn->internal->ske,
474                                  client->internal->params->rekey_secs);
475   if (!connauth) {
476     /** Out of memory */
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;
481   }
482
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,
491                                         fsm));
492 }
493
494 /* Connection fully established */
495
496 SILC_FSM_STATE(silc_client_st_connected)
497 {
498   SilcClientConnection conn = fsm_context;
499   SilcClient client = conn->client;
500
501   SILC_LOG_DEBUG(("Connection established"));
502
503   /* If we connected to server, now register to network. */
504   if (conn->type == SILC_CONN_SERVER &&
505       !conn->internal->params.no_authentication) {
506
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);
512     } else {
513       /** Register to network */
514       silc_fsm_next(fsm, silc_client_st_register);
515     }
516
517     return SILC_FSM_CONTINUE;
518   }
519
520   /* Call connection callback */
521   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
522                  conn->callback_context);
523
524   return SILC_FSM_FINISH;
525 }
526
527 /* Error during connecting */
528
529 SILC_FSM_STATE(silc_client_st_connect_error)
530 {
531   SilcClientConnection conn = fsm_context;
532
533   /* Signal to close connection */
534   conn->internal->disconnected = TRUE;
535   SILC_FSM_SEMA_POST(&conn->internal->wait_event);
536
537   return SILC_FSM_FINISH;
538 }