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