More client library rewrites (key agreement, plus other).
[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 /************************ Static utility functions **************************/
27
28 /* Callback called after connected to remote host */
29
30 static void silc_client_connect_callback(SilcNetStatus status,
31                                          SilcStream stream, void *context)
32 {
33   SilcFSMThread fsm = context;
34   SilcClientConnection conn = silc_fsm_get_context(fsm);
35   SilcClient client = conn->client;
36
37   conn->internal->op = NULL;
38   if (conn->internal->verbose) {
39     switch (status) {
40     case SILC_NET_OK:
41       break;
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",
46                    conn->remote_host);
47       break;
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",
52                    conn->remote_host);
53       break;
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",
58                    conn->remote_host);
59       break;
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",
64                    conn->remote_host);
65       break;
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",
70                    conn->remote_host);
71       break;
72     default:
73       client->internal->ops->say(
74                    client, conn, SILC_CLIENT_MESSAGE_ERROR,
75                    "Could not connect to host %s",
76                    conn->remote_host);
77       break;
78     }
79   }
80
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);
88     return;
89   }
90
91   /* Connection created successfully */
92   SILC_LOG_DEBUG(("Connected"));
93   conn->stream = (void *)stream;
94   SILC_FSM_CALL_CONTINUE(fsm);
95 }
96
97 /* Called after application has verified remote host's public key */
98
99 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
100 {
101   VerifyKeyContext verify = (VerifyKeyContext)context;
102
103   SILC_LOG_DEBUG(("Start"));
104
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);
109
110   silc_free(verify);
111 }
112
113 /* Verify remote host's public key */
114
115 static void silc_client_ke_verify_key(SilcSKE ske,
116                                       SilcPublicKey public_key,
117                                       void *context,
118                                       SilcSKEVerifyCbCompletion completion,
119                                       void *completion_context)
120 {
121   SilcFSMThread fsm = context;
122   SilcClientConnection conn = silc_fsm_get_context(fsm);
123   SilcClient client = conn->client;
124   VerifyKeyContext verify;
125
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,
131                completion_context);
132     return;
133   }
134
135   SILC_LOG_DEBUG(("Verify remote public key"));
136
137   verify = silc_calloc(1, sizeof(*verify));
138   if (!verify) {
139     completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
140                completion_context);
141     return;
142   }
143   verify->ske = ske;
144   verify->completion = completion;
145   verify->completion_context = completion_context;
146
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,
151                                            verify);
152 }
153
154 /* Key exchange protocol completion callback */
155
156 static void silc_client_ke_completion(SilcSKE ske,
157                                       SilcSKEStatus status,
158                                       SilcSKESecurityProperties prop,
159                                       SilcSKEKeyMaterial keymat,
160                                       SilcSKERekeyMaterial rekey,
161                                       void *context)
162 {
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;
168
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));
174
175     if (conn->internal->verbose)
176       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
177                                  "Error during key exchange with %s: %s",
178                                  conn->remote_host,
179                                  silc_ske_map_status(status));
180
181     silc_fsm_next(fsm, silc_client_st_connect_error);
182     SILC_FSM_CALL_CONTINUE(fsm);
183     return;
184   }
185
186   SILC_LOG_DEBUG(("Setting keys into use"));
187
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"));
193
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",
198                        conn->remote_host);
199
200     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
201                    conn->callback_context);
202
203     silc_fsm_next(fsm, silc_client_st_connect_error);
204     SILC_FSM_CALL_CONTINUE(fsm);
205     return;
206   }
207
208   silc_packet_set_ciphers(conn->stream, send_key, receive_key);
209   silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
210
211   conn->internal->rekey = rekey;
212
213   /* Key exchange done */
214   SILC_FSM_CALL_CONTINUE(fsm);
215 }
216
217 /* Callback called by application to return authentication data */
218
219 static void silc_client_connect_auth_method(SilcBool success,
220                                             SilcAuthMethod auth_meth,
221                                             void *auth, SilcUInt32 auth_len,
222                                             void *context)
223 {
224   SilcFSMThread fsm = context;
225   SilcClientConnection conn = silc_fsm_get_context(fsm);
226
227   conn->internal->params.auth_method = SILC_AUTH_NONE;
228
229   if (success) {
230     conn->internal->params.auth_method = auth_meth;
231     conn->internal->params.auth = auth;
232     conn->internal->params.auth_len = auth_len;
233   }
234
235   SILC_FSM_CALL_CONTINUE(fsm);
236 }
237
238 /* Connection authentication completion callback */
239
240 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
241                                                 SilcBool success,
242                                                 void *context)
243 {
244   SilcFSMThread fsm = context;
245   SilcClientConnection conn = silc_fsm_get_context(fsm);
246   SilcClient client = conn->client;
247
248   silc_connauth_free(connauth);
249
250   if (!success) {
251     if (conn->internal->verbose)
252         client->internal->ops->say(
253                         client, conn, SILC_CLIENT_MESSAGE_ERROR,
254                         "Authentication failed");
255
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);
259   }
260
261   SILC_FSM_CALL_CONTINUE(fsm);
262 }
263
264 /* Connection timeout callback */
265
266 SILC_TASK_CALLBACK(silc_client_connect_timeout)
267 {
268   SilcClientConnection conn = context;
269   SilcClient client = conn->client;
270
271   SILC_LOG_DEBUG(("Connection timeout"));
272
273   conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_TIMEOUT, 0, NULL,
274                  conn->callback_context);
275
276   silc_fsm_next(&conn->internal->event_thread, silc_client_st_connect_error);
277   silc_fsm_continue_sync(&conn->internal->event_thread);
278 }
279
280 /*************************** Connect remote host ****************************/
281
282 /* Creates a connection to remote host */
283
284 SILC_FSM_STATE(silc_client_st_connect)
285 {
286   SilcClientConnection conn = fsm_context;
287   SilcClient client = conn->client;
288
289   SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
290                   conn->remote_port));
291
292   /** Connect */
293   silc_fsm_next(fsm, silc_client_st_connect_set_stream);
294
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);
300
301   if (conn->internal->params.udp) {
302     SilcStream stream;
303
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;
311     }
312
313     /* Connect (UDP) */
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);
320
321     SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
322                                                SILC_NET_HOST_UNREACHABLE,
323                                                stream, fsm));
324   } else {
325     /* Connect (TCP) */
326     SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect(
327                                        NULL, conn->remote_host,
328                                        conn->remote_port,
329                                        conn->internal->schedule,
330                                        silc_client_connect_callback, fsm));
331   }
332 }
333
334 /* Sets the new connection stream into use and creates packet stream */
335
336 SILC_FSM_STATE(silc_client_st_connect_set_stream)
337 {
338   SilcClientConnection conn = fsm_context;
339   SilcClient client = conn->client;
340
341   /* Create packet stream */
342   conn->stream = silc_packet_stream_create(client->internal->packet_engine,
343                                            conn->internal->schedule,
344                                            (SilcStream)conn->stream);
345   if (!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;
352   }
353
354   silc_packet_set_context(conn->stream, conn);
355
356   /** Start key exchange */
357   silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
358   return SILC_FSM_CONTINUE;
359 }
360
361 /* Starts key exchange protocol with remote host */
362
363 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
364 {
365   SilcClientConnection conn = fsm_context;
366   SilcClient client = conn->client;
367   SilcSKEParamsStruct params;
368
369   SILC_LOG_DEBUG(("Starting key exchange protocol"));
370
371   /* Allocate SKE */
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) {
377     /** Out of memory */
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;
382   }
383
384   /* Set SKE callbacks */
385   silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
386                          silc_client_ke_completion, fsm);
387
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;
396   }
397
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);
404   else
405     /** Run key exchange (TCP) */
406     silc_fsm_next(fsm, silc_client_st_connect_auth);
407
408   SILC_FSM_CALL(conn->internal->op = silc_ske_initiator(conn->internal->ske,
409                                                         conn->stream,
410                                                         &params, NULL));
411 }
412
413 /* For UDP/IP connections, set up the UDP session after successful key
414    exchange protocol */
415
416 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
417 {
418   SilcClientConnection conn = fsm_context;
419   SilcClient client = conn->client;
420   SilcStream stream, old;
421   SilcSKESecurityProperties prop;
422
423   SILC_LOG_DEBUG(("Setup UDP SILC session"));
424
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);
431   if (!stream) {
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;
437   }
438
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);
445
446   /* Delete the old stream */
447   silc_stream_destroy(old);
448
449   /** Start authentication */
450   silc_fsm_next(fsm, silc_client_st_connect_auth);
451   return SILC_FSM_CONTINUE;
452 }
453
454 /* Get authentication method to be used in authentication protocol */
455
456 SILC_FSM_STATE(silc_client_st_connect_auth)
457 {
458   SilcClientConnection conn = fsm_context;
459   SilcClient client = conn->client;
460
461   SILC_LOG_DEBUG(("Get authentication data"));
462
463   silc_fsm_next(fsm, silc_client_st_connect_auth_start);
464
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(
468                                     client, conn,
469                                     conn->remote_host,
470                                     conn->remote_port,
471                                     silc_client_connect_auth_method, fsm));
472
473   if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
474     conn->internal->params.auth = conn->private_key;
475
476   /* We have authentication data */
477   return SILC_FSM_CONTINUE;
478 }
479
480 /* Start connection authentication with remote host */
481
482 SILC_FSM_STATE(silc_client_st_connect_auth_start)
483 {
484   SilcClientConnection conn = fsm_context;
485   SilcClient client = conn->client;
486   SilcConnAuth connauth;
487
488   SILC_LOG_DEBUG(("Starting connection authentication protocol"));
489
490   /* Allocate connection authentication protocol */
491   connauth = silc_connauth_alloc(conn->internal->schedule,
492                                  conn->internal->ske,
493                                  client->internal->params->rekey_secs);
494   if (!connauth) {
495     /** Out of memory */
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;
500   }
501
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,
510                                         fsm));
511 }
512
513 /* Connection fully established */
514
515 SILC_FSM_STATE(silc_client_st_connected)
516 {
517   SilcClientConnection conn = fsm_context;
518   SilcClient client = conn->client;
519
520   SILC_LOG_DEBUG(("Connection established"));
521
522   /* If we connected to server, now register to network. */
523   if (conn->type == SILC_CONN_SERVER &&
524       !conn->internal->params.no_authentication) {
525
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);
531     } else {
532       /** Register to network */
533       silc_fsm_next(fsm, silc_client_st_register);
534     }
535
536     return SILC_FSM_CONTINUE;
537   }
538
539   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
540
541   /* Call connection callback */
542   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
543                  conn->callback_context);
544
545   return SILC_FSM_FINISH;
546 }
547
548 /* Error during connecting */
549
550 SILC_FSM_STATE(silc_client_st_connect_error)
551 {
552   SilcClientConnection conn = fsm_context;
553
554   /* Signal to close connection */
555   if (!conn->internal->disconnected) {
556     conn->internal->disconnected = TRUE;
557     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
558   }
559
560   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
561
562   return SILC_FSM_FINISH;
563 }