Added debugs.
[crypto.git] / lib / silcclient / client_keyagr.c
1 /*
2
3   client_keyagr.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 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 /* Key agreement context, used by responder */
27 struct SilcClientKeyAgreementStruct {
28   SilcClient client;                      /* Client */
29   SilcClientConnection conn;              /* Server connection */
30   SilcClientListener listener;            /* Listener */
31   SilcKeyAgreementCallback completion;    /* Key agreement completion */
32   void *context;                          /* User context */
33   SilcAsyncOperation op;                  /* Async operation, initiator */
34 };
35
36 /************************ Static utility functions **************************/
37
38 /* Destroyes key agreement session */
39
40 static void silc_client_keyagr_free(SilcClient client,
41                                     SilcClientConnection conn,
42                                     SilcClientEntry client_entry)
43 {
44   SilcClientKeyAgreement ke = client_entry->internal.ke;
45
46   silc_client_listener_free(ke->listener);
47   silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
48   if (ke->op)
49     silc_async_abort(ke->op, NULL, NULL);
50   client_entry->internal.ke = NULL;
51   client_entry->internal.prv_resp = FALSE;
52   silc_client_unref_client(client, conn, client_entry);
53   silc_free(ke);
54 }
55
56 /* Key agreement timeout callback */
57
58 SILC_TASK_CALLBACK(silc_client_keyagr_timeout)
59 {
60   SilcClientEntry client_entry = context;
61   SilcClientKeyAgreement ke = client_entry->internal.ke;
62
63   if (!ke)
64     return;
65
66   SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
67
68   ke->completion(ke->client, ke->conn, client_entry,
69                  SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
70
71   silc_client_keyagr_free(ke->client, ke->conn, client_entry);
72 }
73
74 /* Client resolving callback.  Continues with the key agreement processing */
75
76 static void silc_client_keyagr_resolved(SilcClient client,
77                                         SilcClientConnection conn,
78                                         SilcStatus status,
79                                         SilcDList clients,
80                                         void *context)
81 {
82   /* If no client found, ignore the packet, a silent error */
83   if (!clients)
84     silc_fsm_next(context, silc_client_key_agreement_error);
85
86   /* Continue processing the packet */
87   SILC_FSM_CALL_CONTINUE(context);
88 }
89
90 /* Key exchange completion callback.  Called after connected to remote host
91    and performed key exchange, when we are initiator.  As responder, this is
92    called after the remote has connected to us and have performed the key
93    exchange. */
94
95 static void silc_client_keyagr_completion(SilcClient client,
96                                           SilcClientConnection conn,
97                                           SilcClientConnectionStatus status,
98                                           SilcStatus error,
99                                           const char *message,
100                                           void *context)
101 {
102   SilcClientEntry client_entry = context;
103   SilcClientKeyAgreement ke = client_entry->internal.ke;
104   SilcSKEKeyMaterial keymat;
105
106   ke->op = NULL;
107
108   switch (status) {
109   case SILC_CLIENT_CONN_SUCCESS:
110     SILC_LOG_DEBUG(("Key agreement %p successful", ke));
111
112     keymat = silc_ske_get_key_material(conn->internal->ske);
113     ke->completion(ke->client, ke->conn, client_entry, SILC_KEY_AGREEMENT_OK,
114                    keymat, ke->context);
115     break;
116
117   case SILC_CLIENT_CONN_ERROR_TIMEOUT:
118     SILC_LOG_DEBUG(("Key agreement %p timeout", ke));
119     ke->completion(ke->client, ke->conn, client_entry,
120                    SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
121     break;
122
123   default:
124     SILC_LOG_DEBUG(("Key agreement %p error %d", ke, status));
125     ke->completion(ke->client, ke->conn, client_entry,
126                    SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
127     break;
128   }
129
130   /* Close the connection */
131   if (conn)
132     silc_client_close_connection(ke->client, conn);
133
134   silc_client_keyagr_free(ke->client, ke->conn, client_entry);
135 }
136
137 /*************************** Key Agreement API ******************************/
138
139 /* Sends key agreement packet to remote client.  If IP addresses are provided
140    creates also listener for íncoming key agreement connection.  Supports
141    both TCP and UDP transports. */
142
143 void silc_client_send_key_agreement(SilcClient client,
144                                     SilcClientConnection conn,
145                                     SilcClientEntry client_entry,
146                                     SilcClientConnectionParams *params,
147                                     SilcPublicKey public_key,
148                                     SilcPrivateKey private_key,
149                                     SilcKeyAgreementCallback completion,
150                                     void *context)
151 {
152   SilcClientKeyAgreement ke = NULL;
153   SilcBuffer buffer;
154   SilcUInt16 port = 0, protocol = 0;
155   char *local_ip = NULL;
156
157   SILC_LOG_DEBUG(("Sending key agreement"));
158
159   if (!client_entry)
160     return;
161   if (conn->internal->disconnected)
162     return;
163
164   if (client_entry->internal.ke) {
165     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED,
166                NULL, context);
167     return;
168   }
169
170   if (client_entry == conn->local_entry) {
171     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
172                NULL, context);
173     return;
174   }
175
176   /* If local IP is provided, create listener.  If this is not provided,
177      we'll just send empty key agreement payload */
178   if (params && (params->local_ip || params->bind_ip)) {
179     ke = silc_calloc(1, sizeof(*ke));
180     if (!ke) {
181       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
182                  NULL, context);
183       return;
184     }
185
186     /* Create listener */
187     ke->listener = silc_client_listener_add(client, conn->internal->schedule,
188                                             params, public_key, private_key,
189                                             silc_client_keyagr_completion,
190                                             client_entry);
191     if (!ke->listener) {
192       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
193                  NULL, context);
194       return;
195     }
196
197     local_ip = params->local_ip;
198     protocol = params->udp;
199
200     ke->client = client;
201     ke->conn = conn;
202     ke->completion = completion;
203     ke->context = context;
204     silc_client_ref_client(client, conn, client_entry);
205     client_entry->internal.ke = ke;
206     client_entry->internal.prv_resp = TRUE;
207   }
208
209   /* Encode the key agreement payload */
210   buffer = silc_key_agreement_payload_encode(local_ip, protocol, port);
211   if (!buffer) {
212     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
213                NULL, context);
214     silc_client_keyagr_free(client, conn, client_entry);
215     return;
216   }
217
218   /* Send the key agreement packet to the client */
219   if (!silc_packet_send_ext(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0,
220                             0, NULL, SILC_ID_CLIENT, &client_entry->id,
221                             silc_buffer_datalen(buffer), NULL, NULL)) {
222     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
223                NULL, context);
224     silc_client_keyagr_free(client, conn, client_entry);
225     silc_buffer_free(buffer);
226     return;
227   }
228
229   /* Add key agreement timeout task */
230   if (params && params->timeout_secs)
231     silc_schedule_task_add_timeout(conn->internal->schedule,
232                                    silc_client_keyagr_timeout,
233                                    client_entry, params->timeout_secs, 0);
234
235   silc_buffer_free(buffer);
236 }
237
238 /* Perform key agreement protocol as initiator.  Conneects to remote host. */
239
240 void silc_client_perform_key_agreement(SilcClient client,
241                                        SilcClientConnection conn,
242                                        SilcClientEntry client_entry,
243                                        SilcClientConnectionParams *params,
244                                        SilcPublicKey public_key,
245                                        SilcPrivateKey private_key,
246                                        char *hostname, int port,
247                                        SilcKeyAgreementCallback completion,
248                                        void *context)
249 {
250   SilcClientKeyAgreement ke;
251
252   SILC_LOG_DEBUG(("Performing key agreement"));
253
254   if (!client_entry || !hostname || !port) {
255     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
256                NULL, context);
257     return;
258   }
259
260   if (client_entry == conn->local_entry) {
261     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
262                NULL, context);
263     return;
264   }
265
266   ke = silc_calloc(1, sizeof(*ke));
267   if (!ke) {
268     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
269                NULL, context);
270     return;
271   }
272   ke->client = client;
273   ke->conn = conn;
274   ke->completion = completion;
275   ke->context = context;
276   silc_client_ref_client(client, conn, client_entry);
277   client_entry->internal.ke = ke;
278
279   if (params)
280     params->no_authentication = TRUE;
281
282   /* Connect to the remote client.  Performs key exchange automatically. */
283   ke->op = silc_client_connect_to_client(client, params, public_key,
284                                          private_key, hostname, port,
285                                          silc_client_keyagr_completion,
286                                          client_entry);
287   if (!ke->op) {
288     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
289                NULL, context);
290     silc_client_keyagr_free(client, conn, client_entry);
291     return;
292   }
293 }
294
295 /* Same as above but caller has created connection. */
296
297 void
298 silc_client_perform_key_agreement_stream(SilcClient client,
299                                          SilcClientConnection conn,
300                                          SilcClientEntry client_entry,
301                                          SilcClientConnectionParams *params,
302                                          SilcPublicKey public_key,
303                                          SilcPrivateKey private_key,
304                                          SilcStream stream,
305                                          SilcKeyAgreementCallback completion,
306                                          void *context)
307 {
308   SilcClientKeyAgreement ke;
309
310   SILC_LOG_DEBUG(("Performing key agreement"));
311
312   if (!client_entry || !stream) {
313     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
314                NULL, context);
315     return;
316   }
317
318   if (client_entry == conn->local_entry) {
319     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
320                NULL, context);
321     return;
322   }
323
324   ke = silc_calloc(1, sizeof(*ke));
325   if (!ke) {
326     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
327                NULL, context);
328     return;
329   }
330   ke->client = client;
331   ke->conn = conn;
332   ke->completion = completion;
333   ke->context = context;
334   silc_client_ref_client(client, conn, client_entry);
335   client_entry->internal.ke = ke;
336
337   if (params)
338     params->no_authentication = TRUE;
339
340   /* Perform key exchange protocol */
341   ke->op = silc_client_key_exchange(client, params, public_key,
342                                     private_key, stream, SILC_CONN_CLIENT,
343                                     silc_client_keyagr_completion,
344                                     client_entry);
345   if (!ke->op) {
346     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
347                NULL, context);
348     silc_client_keyagr_free(client, conn, client_entry);
349     return;
350   }
351 }
352
353 /* This function can be called to unbind the hostname and the port for
354    the key agreement protocol. However, this function has effect only
355    before the key agreement protocol has been performed. After it has
356    been performed the library will automatically unbind the port. The
357    `client_entry' is the client to which we sent the key agreement
358    request. */
359
360 void silc_client_abort_key_agreement(SilcClient client,
361                                      SilcClientConnection conn,
362                                      SilcClientEntry client_entry)
363 {
364   SilcClientKeyAgreement ke;
365
366   if (!client_entry || !client_entry->internal.ke)
367     return;
368
369   ke = client_entry->internal.ke;
370
371   SILC_LOG_DEBUG(("Abort key agreement %p"));
372
373   ke->completion(client, conn, client_entry,
374                  SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
375
376   silc_client_keyagr_free(client, conn, client_entry);
377 }
378
379 /* Key agreement packet received */
380
381 SILC_FSM_STATE(silc_client_key_agreement)
382 {
383   SilcClientConnection conn = fsm_context;
384   SilcClient client = conn->client;
385   SilcPacket packet = state_context;
386   SilcClientID remote_id;
387   SilcClientEntry remote_client;
388   SilcKeyAgreementPayload payload;
389
390   if (packet->src_id_type != SILC_ID_CLIENT) {
391     /** Invalid packet */
392     silc_fsm_next(fsm, silc_client_key_agreement_error);
393     return SILC_FSM_CONTINUE;
394   }
395
396   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
397                       &remote_id, sizeof(remote_id))) {
398     /** Invalid source ID */
399     silc_fsm_next(fsm, silc_client_key_agreement_error);
400     return SILC_FSM_CONTINUE;
401   }
402
403   /* Check whether we know this client already */
404   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
405   if (!remote_client || !remote_client->internal.valid) {
406     /** Resolve client info */
407     silc_client_unref_client(client, conn, remote_client);
408     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
409                                          client, conn, &remote_id, NULL,
410                                          silc_client_keyagr_resolved, fsm));
411     /* NOT REACHED */
412   }
413
414   /* Parse the key agreement payload */
415   payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer),
416                                              silc_buffer_len(&packet->buffer));
417   if (!payload) {
418     /** Malformed Payload */
419     SILC_LOG_DEBUG(("Malformed key agreement payload"));
420     silc_fsm_next(fsm, silc_client_key_agreement_error);
421     return SILC_FSM_CONTINUE;
422   }
423
424   /* If remote did not provide connection endpoint, we will assume that we
425      will provide it and will be responder. */
426   if (!silc_key_agreement_get_hostname(payload))
427     remote_client->internal.prv_resp = TRUE;
428   else
429     remote_client->internal.prv_resp = FALSE;
430
431   /* Notify application for key agreement request */
432   client->internal->ops->key_agreement(
433                                    client, conn, remote_client,
434                                    silc_key_agreement_get_hostname(payload),
435                                    silc_key_agreement_get_protocol(payload),
436                                    silc_key_agreement_get_port(payload));
437
438   silc_key_agreement_payload_free(payload);
439
440   silc_packet_free(packet);
441   return SILC_FSM_FINISH;
442 }
443
444 /* Key agreement packet processing error */
445
446 SILC_FSM_STATE(silc_client_key_agreement_error)
447 {
448   SilcPacket packet = state_context;
449   silc_packet_free(packet);
450   return SILC_FSM_FINISH;
451 }