silcclient: Add generic client entry operation context
[silc.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 - 2014 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 };
34
35 /************************ Static utility functions **************************/
36
37 /* Destroyes key agreement session */
38
39 static void silc_client_keyagr_free(SilcClient client,
40                                     SilcClientConnection conn,
41                                     SilcClientEntry client_entry)
42 {
43   SilcClientKeyAgreement ke = client_entry->internal.ke;
44
45   silc_client_listener_free(ke->listener);
46   silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
47   if (client_entry->internal.op)
48     silc_async_abort(client_entry->internal.op, NULL, NULL);
49   client_entry->internal.op = 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   client_entry->internal.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   client_entry->internal.op =
284     silc_client_connect_to_client(client, params, public_key,
285                                   private_key, hostname, port,
286                                   silc_client_keyagr_completion,
287                                   client_entry);
288   if (!client_entry->internal.op) {
289     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
290                NULL, context);
291     silc_client_keyagr_free(client, conn, client_entry);
292     return;
293   }
294 }
295
296 /* Same as above but caller has created connection. */
297
298 void
299 silc_client_perform_key_agreement_stream(SilcClient client,
300                                          SilcClientConnection conn,
301                                          SilcClientEntry client_entry,
302                                          SilcClientConnectionParams *params,
303                                          SilcPublicKey public_key,
304                                          SilcPrivateKey private_key,
305                                          SilcStream stream,
306                                          SilcKeyAgreementCallback completion,
307                                          void *context)
308 {
309   SilcClientKeyAgreement ke;
310
311   SILC_LOG_DEBUG(("Performing key agreement"));
312
313   if (!client_entry || !stream) {
314     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
315                NULL, context);
316     return;
317   }
318
319   if (client_entry == conn->local_entry) {
320     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_SELF_DENIED,
321                NULL, context);
322     return;
323   }
324
325   ke = silc_calloc(1, sizeof(*ke));
326   if (!ke) {
327     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
328                NULL, context);
329     return;
330   }
331   ke->client = client;
332   ke->conn = conn;
333   ke->completion = completion;
334   ke->context = context;
335   silc_client_ref_client(client, conn, client_entry);
336   client_entry->internal.ke = ke;
337
338   if (params)
339     params->no_authentication = TRUE;
340
341   /* Perform key exchange protocol */
342   client_entry->internal.op =
343     silc_client_key_exchange(client, params, public_key,
344                              private_key, stream, SILC_CONN_CLIENT,
345                              silc_client_keyagr_completion,
346                              client_entry);
347   if (!client_entry->internal.op) {
348     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
349                NULL, context);
350     silc_client_keyagr_free(client, conn, client_entry);
351     return;
352   }
353 }
354
355 /* This function can be called to unbind the hostname and the port for
356    the key agreement protocol. However, this function has effect only
357    before the key agreement protocol has been performed. After it has
358    been performed the library will automatically unbind the port. The
359    `client_entry' is the client to which we sent the key agreement
360    request. */
361
362 void silc_client_abort_key_agreement(SilcClient client,
363                                      SilcClientConnection conn,
364                                      SilcClientEntry client_entry)
365 {
366   SilcClientKeyAgreement ke;
367
368   if (!client_entry || !client_entry->internal.ke)
369     return;
370
371   ke = client_entry->internal.ke;
372
373   SILC_LOG_DEBUG(("Abort key agreement ke %p for client %p on connection %p",
374                   ke, client, conn));
375
376   ke->completion(client, conn, client_entry,
377                  SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
378
379   silc_client_keyagr_free(client, conn, client_entry);
380 }
381
382 /* Key agreement packet received */
383
384 SILC_FSM_STATE(silc_client_key_agreement)
385 {
386   SilcClientConnection conn = fsm_context;
387   SilcClient client = conn->client;
388   SilcPacket packet = state_context;
389   SilcClientID remote_id;
390   SilcClientEntry remote_client;
391   SilcKeyAgreementPayload payload;
392
393   if (packet->src_id_type != SILC_ID_CLIENT) {
394     /** Invalid packet */
395     silc_fsm_next(fsm, silc_client_key_agreement_error);
396     return SILC_FSM_CONTINUE;
397   }
398
399   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
400                       &remote_id, sizeof(remote_id))) {
401     /** Invalid source ID */
402     silc_fsm_next(fsm, silc_client_key_agreement_error);
403     return SILC_FSM_CONTINUE;
404   }
405
406   /* Check whether we know this client already */
407   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
408   if (!remote_client || !remote_client->internal.valid) {
409     /** Resolve client info */
410     silc_client_unref_client(client, conn, remote_client);
411     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
412                                          client, conn, &remote_id, NULL,
413                                          silc_client_keyagr_resolved, fsm));
414     /* NOT REACHED */
415   }
416
417   /* Parse the key agreement payload */
418   payload = silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer),
419                                              silc_buffer_len(&packet->buffer));
420   if (!payload) {
421     /** Malformed Payload */
422     SILC_LOG_DEBUG(("Malformed key agreement payload"));
423     silc_fsm_next(fsm, silc_client_key_agreement_error);
424     return SILC_FSM_CONTINUE;
425   }
426
427   /* If remote did not provide connection endpoint, we will assume that we
428      will provide it and will be responder. */
429   if (!silc_key_agreement_get_hostname(payload))
430     remote_client->internal.prv_resp = TRUE;
431   else
432     remote_client->internal.prv_resp = FALSE;
433
434   /* Notify application for key agreement request */
435   client->internal->ops->key_agreement(
436                                    client, conn, remote_client,
437                                    silc_key_agreement_get_hostname(payload),
438                                    silc_key_agreement_get_protocol(payload),
439                                    silc_key_agreement_get_port(payload));
440
441   silc_key_agreement_payload_free(payload);
442
443   silc_packet_free(packet);
444   return SILC_FSM_FINISH;
445 }
446
447 /* Key agreement packet processing error */
448
449 SILC_FSM_STATE(silc_client_key_agreement_error)
450 {
451   SilcPacket packet = state_context;
452   silc_packet_free(packet);
453   return SILC_FSM_FINISH;
454 }