1 /*
2
3 client_keyagr.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2001 - 2014 Pekka Riikonen
8
9 The contents of this file are subject to one of the Licenses specified
10 in the COPYING file; You may not use this file except in compliance
11 with the License.
12
13 The software distributed under the License is distributed on an "AS IS"
14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15 KIND, either expressed or implied. See the COPYING file for more
16 information.
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
silc_client_keyagr_free(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry)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
SILC_TASK_CALLBACK(silc_client_keyagr_timeout)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
silc_client_keyagr_resolved(SilcClient client,SilcClientConnection conn,SilcStatus status,SilcDList clients,void * context)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
silc_client_keyagr_completion(SilcClient client,SilcClientConnection conn,SilcClientConnectionStatus status,SilcStatus error,const char * message,void * context)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
silc_client_send_key_agreement(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcClientConnectionParams * params,SilcPublicKey public_key,SilcPrivateKey private_key,SilcKeyAgreementCallback completion,void * context)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
silc_client_perform_key_agreement(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcClientConnectionParams * params,SilcPublicKey public_key,SilcPrivateKey private_key,char * hostname,int port,SilcKeyAgreementCallback completion,void * context)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
silc_client_perform_key_agreement_stream(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcClientConnectionParams * params,SilcPublicKey public_key,SilcPrivateKey private_key,SilcStream stream,SilcKeyAgreementCallback completion,void * context)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
silc_client_abort_key_agreement(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry)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
SILC_FSM_STATE(silc_client_key_agreement)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
SILC_FSM_STATE(silc_client_key_agreement_error)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 }
455