Initial client library rewrite, connects to remote server already.
[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, conn->context);
92     silc_fsm_next(fsm, silc_client_st_connect_error);
93     SILC_FSM_CALL_CONTINUE(fsm);
94     return;
95   }
96
97   SILC_LOG_DEBUG(("Connected"));
98
99   /* Create packet stream */
100   conn->stream = silc_packet_stream_create(client->internal->packet_engine,
101                                            conn->internal->schedule, stream);
102   if (!conn->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);
107     return;
108   }
109
110   silc_packet_set_context(conn->stream, conn);
111
112   /* Connection created successfully */
113   SILC_FSM_CALL_CONTINUE(fsm);
114 }
115
116 /* Called after application has verified remote host's public key */
117
118 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
119 {
120   VerifyKeyContext verify = (VerifyKeyContext)context;
121
122   SILC_LOG_DEBUG(("Start"));
123
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);
128
129   silc_free(verify);
130 }
131
132 /* Verify remote host's public key */
133
134 static void silc_client_ke_verify_key(SilcSKE ske,
135                                       SilcPublicKey public_key,
136                                       void *context,
137                                       SilcSKEVerifyCbCompletion completion,
138                                       void *completion_context)
139 {
140   SilcFSMThread fsm = context;
141   SilcClientConnection conn = silc_fsm_get_context(fsm);
142   SilcClient client = conn->client;
143   VerifyKeyContext verify;
144
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,
150                completion_context);
151     return;
152   }
153
154   SILC_LOG_DEBUG(("Verify remote public key"));
155
156   verify = silc_calloc(1, sizeof(*verify));
157   if (!verify) {
158     completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
159                completion_context);
160     return;
161   }
162   verify->ske = ske;
163   verify->completion = completion;
164   verify->completion_context = completion_context;
165
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,
170                                            verify);
171 }
172
173 /* Key exchange protocol completion callback */
174
175 static void silc_client_ke_completion(SilcSKE ske,
176                                       SilcSKEStatus status,
177                                       SilcSKESecurityProperties prop,
178                                       SilcSKEKeyMaterial keymat,
179                                       SilcSKERekeyMaterial rekey,
180                                       void *context)
181 {
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;
187
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));
192
193     if (conn->internal->verbose)
194       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
195                                  "Error during key exchange with %s: %s",
196                                  conn->remote_host,
197                                  silc_ske_map_status(status));
198
199     silc_fsm_next(fsm, silc_client_st_connect_error);
200     SILC_FSM_CALL_CONTINUE(fsm);
201     return;
202   }
203
204   SILC_LOG_DEBUG(("Setting keys into use"));
205
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"));
211
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",
216                        conn->remote_host);
217
218     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, conn->context);
219
220     silc_fsm_next(fsm, silc_client_st_connect_error);
221     SILC_FSM_CALL_CONTINUE(fsm);
222     return;
223   }
224
225   silc_packet_set_ciphers(conn->stream, send_key, receive_key);
226   silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
227
228   conn->internal->rekey = rekey;
229
230   /* Key exchange done */
231   SILC_FSM_CALL_CONTINUE(fsm);
232 }
233
234 /* Callback called by application to return authentication data */
235
236 static void silc_client_connect_auth_method(SilcBool success,
237                                             SilcAuthMethod auth_meth,
238                                             void *auth, SilcUInt32 auth_len,
239                                             void *context)
240 {
241   SilcFSMThread fsm = context;
242   SilcClientConnection conn = silc_fsm_get_context(fsm);
243
244   conn->internal->params.auth_method = SILC_AUTH_NONE;
245
246   if (success) {
247     conn->internal->params.auth_method = auth_meth;
248     conn->internal->params.auth = auth;
249     conn->internal->params.auth_len = auth_len;
250   }
251
252   SILC_FSM_CALL_CONTINUE(fsm);
253 }
254
255 /* Connection authentication completion callback */
256
257 static void silc_client_connect_auth_completion(SilcConnAuth connauth,
258                                                 SilcBool success,
259                                                 void *context)
260 {
261   SilcFSMThread fsm = context;
262   SilcClientConnection conn = silc_fsm_get_context(fsm);
263   SilcClient client = conn->client;
264
265   silc_connauth_free(connauth);
266
267   if (!success) {
268     if (conn->internal->verbose)
269         client->internal->ops->say(
270                         client, conn, SILC_CLIENT_MESSAGE_ERROR,
271                         "Authentication failed");
272
273     conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, conn->context);
274     silc_fsm_next(fsm, silc_client_st_connect_error);
275   }
276
277   SILC_FSM_CALL_CONTINUE(fsm);
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   silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
293
294   if (conn->internal->params.udp) {
295     SilcStream stream;
296
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;
303     }
304
305     /** Connect (UDP) */
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);
310
311     SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
312                                                SILC_NET_HOST_UNREACHABLE,
313                                                stream, fsm));
314   } else {
315     /** Connect (TCP) */
316     SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host,
317                                        conn->remote_port,
318                                        conn->internal->schedule,
319                                        silc_client_connect_callback, fsm));
320   }
321 }
322
323 /* Starts key exchange protocol with remote host */
324
325 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
326 {
327   SilcClientConnection conn = fsm_context;
328   SilcClient client = conn->client;
329   SilcSKEParamsStruct params;
330
331   SILC_LOG_DEBUG(("Starting key exchange protocol"));
332
333   /* Allocate SKE */
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) {
339     /** Out of memory */
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;
343   }
344
345   /* Set SKE callbacks */
346   silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
347                          silc_client_ke_completion, fsm);
348
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;
357   }
358
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);
364   else
365     silc_fsm_next(fsm, silc_client_st_connect_auth);
366
367   SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream,
368                                    &params, NULL));
369 }
370
371 /* For UDP/IP connections, set up the UDP session after successful key
372    exchange protocol */
373
374 SILC_FSM_STATE(silc_client_st_connect_setup_udp)
375 {
376   SilcClientConnection conn = fsm_context;
377   SilcClient client = conn->client;
378   SilcStream stream, old;
379   SilcSKESecurityProperties prop;
380
381   SILC_LOG_DEBUG(("Setup UDP SILC session"));
382
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);
389   if (!stream) {
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;
394   }
395
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);
402
403   /* Delete the old stream */
404   silc_stream_destroy(old);
405
406   /** Start authentication */
407   silc_fsm_next(fsm, silc_client_st_connect_auth);
408   return SILC_FSM_CONTINUE;
409 }
410
411 /* Get authentication method to be used in authentication protocol */
412
413 SILC_FSM_STATE(silc_client_st_connect_auth)
414 {
415   SilcClientConnection conn = fsm_context;
416   SilcClient client = conn->client;
417
418   SILC_LOG_DEBUG(("Get authentication data"));
419
420   silc_fsm_next(fsm, silc_client_st_connect_auth_start);
421
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));
428
429   if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
430     conn->internal->params.auth = conn->private_key;
431
432   /* We have authentication data */
433   return SILC_FSM_CONTINUE;
434 }
435
436 /* Start connection authentication with remote host */
437
438 SILC_FSM_STATE(silc_client_st_connect_auth_start)
439 {
440   SilcClientConnection conn = fsm_context;
441   SilcClient client = conn->client;
442   SilcConnAuth connauth;
443
444   SILC_LOG_DEBUG(("Starting connection authentication protocol"));
445
446   /* Allocate connection authentication protocol */
447   connauth = silc_connauth_alloc(conn->internal->schedule,
448                                  conn->internal->ske,
449                                  client->internal->params->rekey_secs);
450   if (!connauth) {
451     /** Out of memory */
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;
455   }
456
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,
464                                         fsm));
465 }
466
467 /* Connection fully established */
468
469 SILC_FSM_STATE(silc_client_st_connected)
470 {
471   SilcClientConnection conn = fsm_context;
472   SilcClient client = conn->client;
473
474   SILC_LOG_DEBUG(("Connection established"));
475
476   /* If we connected to server, now register to network. */
477   if (conn->type == SILC_CONN_SERVER &&
478       !conn->internal->params.no_authentication) {
479
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);
485     } else {
486       /** Register to network */
487       silc_fsm_next(fsm, silc_client_st_register);
488     }
489
490     return SILC_FSM_CONTINUE;
491   }
492
493   /* Call connection callback */
494   conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, conn->context);
495
496   return SILC_FSM_FINISH;
497 }
498
499 /* Error during connecting */
500
501 SILC_FSM_STATE(silc_client_st_connect_error)
502 {
503
504   /* XXX */
505   /* Close connection */
506
507   return SILC_FSM_FINISH;
508 }