78732162897f3848ead1d8ef728cb434f3668956
[strongswan.git] / src / charon / plugins / ha_sync / ha_sync_dispatcher.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * $Id$
16 */
17
18 #include "ha_sync_dispatcher.h"
19
20 #include <daemon.h>
21 #include <processing/jobs/callback_job.h>
22
23 typedef struct private_ha_sync_dispatcher_t private_ha_sync_dispatcher_t;
24
25 /**
26 * Private data of an ha_sync_dispatcher_t object.
27 */
28 struct private_ha_sync_dispatcher_t {
29
30 /**
31 * Public ha_sync_dispatcher_t interface.
32 */
33 ha_sync_dispatcher_t public;
34
35 /**
36 * socket to pull messages from
37 */
38 ha_sync_socket_t *socket;
39
40 /**
41 * Synced SA state cache
42 */
43 ha_sync_cache_t *cache;
44
45 /**
46 * Dispatcher job
47 */
48 callback_job_t *job;
49 };
50
51 /**
52 * Quick and dirty hack implementation of diffie_hellman_t.get_shared_secret
53 */
54 static status_t get_shared_secret(diffie_hellman_t *this, chunk_t *secret)
55 {
56 *secret = chunk_clone((*(chunk_t*)this->destroy));
57 return SUCCESS;
58 }
59
60 /**
61 * Process messages of type IKE_ADD
62 */
63 static void process_ike_add(private_ha_sync_dispatcher_t *this,
64 ha_sync_message_t *message)
65 {
66 ha_sync_message_attribute_t attribute;
67 ha_sync_message_value_t value;
68 enumerator_t *enumerator;
69 ike_sa_t *ike_sa = NULL, *old_sa = NULL;
70 u_int16_t encr = 0, len = 0, integ = 0, prf = 0, old_prf = PRF_UNDEFINED;
71 chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty;
72 chunk_t secret = chunk_empty, old_skd = chunk_empty;
73
74 enumerator = message->create_attribute_enumerator(message);
75 while (enumerator->enumerate(enumerator, &attribute, &value))
76 {
77 switch (attribute)
78 {
79 case HA_SYNC_IKE_ID:
80 ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id);
81 DBG2(DBG_IKE, "got HA_SYNC_IKE_ADD: %llx:%llx - %p",
82 value.ike_sa_id->get_initiator_spi(value.ike_sa_id),
83 value.ike_sa_id->get_responder_spi(value.ike_sa_id),
84 ike_sa);
85 break;
86 case HA_SYNC_IKE_REKEY_ID:
87 old_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id);
88 break;
89 case HA_SYNC_NONCE_I:
90 nonce_i = value.chunk;
91 break;
92 case HA_SYNC_NONCE_R:
93 nonce_r = value.chunk;
94 break;
95 case HA_SYNC_SECRET:
96 secret = value.chunk;
97 break;
98 case HA_SYNC_OLD_SKD:
99 old_skd = value.chunk;
100 break;
101 case HA_SYNC_ALG_ENCR:
102 encr = value.u16;
103 break;
104 case HA_SYNC_ALG_ENCR_LEN:
105 len = value.u16;
106 break;
107 case HA_SYNC_ALG_INTEG:
108 integ = value.u16;
109 break;
110 case HA_SYNC_ALG_PRF:
111 prf = value.u16;
112 break;
113 case HA_SYNC_ALG_OLD_PRF:
114 old_prf = value.u16;
115 break;
116 default:
117 break;
118 }
119 }
120 enumerator->destroy(enumerator);
121
122 if (ike_sa)
123 {
124 proposal_t *proposal;
125 keymat_t *keymat;
126 /* quick and dirty hack of a DH implementation ;-) */
127 diffie_hellman_t dh = { .get_shared_secret = get_shared_secret,
128 .destroy = (void*)&secret };
129
130 proposal = proposal_create(PROTO_IKE);
131 keymat = ike_sa->get_keymat(ike_sa);
132 if (integ)
133 {
134 proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
135 }
136 if (encr)
137 {
138 proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
139 }
140 if (prf)
141 {
142 proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0);
143 }
144 charon->bus->set_sa(charon->bus, ike_sa);
145 if (!keymat->derive_ike_keys(keymat, proposal, &dh, nonce_i, nonce_r,
146 ike_sa->get_id(ike_sa), old_prf, old_skd))
147 {
148 DBG1(DBG_IKE, "HA sync keymat derivation failed");
149 }
150 charon->bus->set_sa(charon->bus, NULL);
151 proposal->destroy(proposal);
152
153 if (old_sa)
154 {
155 ike_sa->inherit(ike_sa, old_sa);
156 this->cache->delete_ike_sa(this->cache, old_sa->get_id(old_sa));
157 }
158 }
159 }
160
161 /**
162 * Apply a condition flag to the IKE_SA if it is in set
163 */
164 static void set_condition(ike_sa_t *ike_sa, ike_condition_t set,
165 ike_condition_t flag)
166 {
167 ike_sa->set_condition(ike_sa, flag, flag & set);
168 }
169
170 /**
171 * Apply a extension flag to the IKE_SA if it is in set
172 */
173 static void set_extension(ike_sa_t *ike_sa, ike_extension_t set,
174 ike_extension_t flag)
175 {
176 if (flag & set)
177 {
178 ike_sa->enable_extension(ike_sa, flag);
179 }
180 }
181
182 /**
183 * Process messages of type IKE_UPDATE
184 */
185 static void process_ike_update(private_ha_sync_dispatcher_t *this,
186 ha_sync_message_t *message)
187 {
188 ha_sync_message_attribute_t attribute;
189 ha_sync_message_value_t value;
190 enumerator_t *enumerator;
191 ike_sa_t *ike_sa = NULL;
192 peer_cfg_t *peer_cfg = NULL;
193
194 enumerator = message->create_attribute_enumerator(message);
195 while (enumerator->enumerate(enumerator, &attribute, &value))
196 {
197 if (attribute != HA_SYNC_IKE_ID && ike_sa == NULL)
198 {
199 DBG1(DBG_IKE, "HA_SYNC_IKE_ID must be first attribute");
200 break;
201 }
202 switch (attribute)
203 {
204 case HA_SYNC_IKE_ID:
205 ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id);
206 DBG2(DBG_IKE, "got HA_SYNC_IKE_UPDATE: %llx:%llx - %p",
207 value.ike_sa_id->get_initiator_spi(value.ike_sa_id),
208 value.ike_sa_id->get_responder_spi(value.ike_sa_id),
209 ike_sa);
210 break;
211 case HA_SYNC_LOCAL_ID:
212 ike_sa->set_my_id(ike_sa, value.id->clone(value.id));
213 break;
214 case HA_SYNC_REMOTE_ID:
215 ike_sa->set_other_id(ike_sa, value.id->clone(value.id));
216 break;
217 case HA_SYNC_EAP_ID:
218 ike_sa->set_eap_identity(ike_sa, value.id->clone(value.id));
219 break;
220 case HA_SYNC_LOCAL_ADDR:
221 ike_sa->set_my_host(ike_sa, value.host->clone(value.host));
222 break;
223 case HA_SYNC_REMOTE_ADDR:
224 ike_sa->set_other_host(ike_sa, value.host->clone(value.host));
225 break;
226 case HA_SYNC_LOCAL_VIP:
227 case HA_SYNC_REMOTE_VIP:
228 ike_sa->set_virtual_ip(ike_sa, attribute == HA_SYNC_LOCAL_VIP,
229 value.host->clone(value.host));
230 break;
231 case HA_SYNC_ADDITIONAL_ADDR:
232 ike_sa->add_additional_address(ike_sa,
233 value.host->clone(value.host));
234 break;
235 case HA_SYNC_CONFIG_NAME:
236 peer_cfg = charon->backends->get_peer_cfg_by_name(
237 charon->backends, value.str);
238 break;
239 case HA_SYNC_CONDITIONS:
240 set_condition(ike_sa, value.u32, EXT_NATT);
241 set_condition(ike_sa, value.u32, EXT_MOBIKE);
242 set_condition(ike_sa, value.u32, EXT_HASH_AND_URL);
243 break;
244 case HA_SYNC_EXTENSIONS:
245 set_extension(ike_sa, value.u32, COND_NAT_ANY);
246 set_extension(ike_sa, value.u32, COND_NAT_HERE);
247 set_extension(ike_sa, value.u32, COND_NAT_THERE);
248 set_extension(ike_sa, value.u32, COND_NAT_FAKE);
249 set_extension(ike_sa, value.u32, COND_EAP_AUTHENTICATED);
250 set_extension(ike_sa, value.u32, COND_CERTREQ_SEEN);
251 set_extension(ike_sa, value.u32, COND_ORIGINAL_INITIATOR);
252 break;
253 default:
254 break;
255 }
256 }
257 enumerator->destroy(enumerator);
258
259 if (peer_cfg)
260 {
261 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
262 peer_cfg->destroy(peer_cfg);
263
264 charon->bus->set_sa(charon->bus, ike_sa);
265 /* we use the CONNECTING state to indicate sync is complete. */
266 /* TODO: add an additional HOT_COPY state? */
267 ike_sa->set_state(ike_sa, IKE_CONNECTING);
268 charon->bus->set_sa(charon->bus, NULL);
269 }
270 else
271 {
272 DBG1(DBG_IKE, "HA sync is missing nodes peer configuration");
273 }
274 }
275
276 /**
277 * Process messages of type IKE_DELETE
278 */
279 static void process_ike_delete(private_ha_sync_dispatcher_t *this,
280 ha_sync_message_t *message)
281 {
282 ha_sync_message_attribute_t attribute;
283 ha_sync_message_value_t value;
284 enumerator_t *enumerator;
285
286 enumerator = message->create_attribute_enumerator(message);
287 while (enumerator->enumerate(enumerator, &attribute, &value))
288 {
289 switch (attribute)
290 {
291 case HA_SYNC_IKE_ID:
292 DBG2(DBG_IKE, "got HA_SYNC_IKE_DELETE: %llx:%llx",
293 value.ike_sa_id->get_initiator_spi(value.ike_sa_id),
294 value.ike_sa_id->get_responder_spi(value.ike_sa_id));
295 this->cache->delete_ike_sa(this->cache, value.ike_sa_id);
296 break;
297 default:
298 break;
299 }
300 }
301 enumerator->destroy(enumerator);
302 }
303
304
305 /**
306 * get the child_cfg with the same name as the peer cfg
307 */
308 static child_cfg_t* find_child_cfg(char *name)
309 {
310 peer_cfg_t *peer_cfg;
311 child_cfg_t *current, *found = NULL;
312 enumerator_t *enumerator;
313
314 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
315 if (peer_cfg)
316 {
317 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
318 while (enumerator->enumerate(enumerator, &current))
319 {
320 if (streq(current->get_name(current), name))
321 {
322 found = current;
323 found->get_ref(found);
324 break;
325 }
326 }
327 enumerator->destroy(enumerator);
328 peer_cfg->destroy(peer_cfg);
329 }
330 return found;
331 }
332
333 /**
334 * Process messages of type CHILD_ADD
335 */
336 static void process_child_add(private_ha_sync_dispatcher_t *this,
337 ha_sync_message_t *message)
338 {
339 ha_sync_message_attribute_t attribute;
340 ha_sync_message_value_t value;
341 enumerator_t *enumerator;
342 ike_sa_t *ike_sa = NULL;
343 child_cfg_t *config = NULL;
344 child_sa_t *child_sa;
345 proposal_t *proposal;
346 keymat_t *keymat;
347 bool initiator, failed = FALSE;
348 u_int32_t inbound_spi = 0, outbound_spi = 0;
349 u_int16_t inbound_cpi = 0, outbound_cpi = 0;
350 u_int8_t mode = MODE_TUNNEL, ipcomp = 0;
351 u_int16_t encr = ENCR_UNDEFINED, integ = AUTH_UNDEFINED, len = 0;
352 chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty;
353 chunk_t encr_i, integ_i, encr_r, integ_r;
354 linked_list_t *local_ts, *remote_ts;
355 /* quick and dirty hack of a DH implementation */
356 diffie_hellman_t dh = { .get_shared_secret = get_shared_secret,
357 .destroy = (void*)&secret };
358
359 enumerator = message->create_attribute_enumerator(message);
360 while (enumerator->enumerate(enumerator, &attribute, &value))
361 {
362 switch (attribute)
363 {
364 case HA_SYNC_IKE_ID:
365 ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id);
366 initiator = value.ike_sa_id->is_initiator(value.ike_sa_id);
367 DBG2(DBG_CHD, "got HA_SYNC_CHILD_ADD: %llx:%llx - %p",
368 value.ike_sa_id->get_initiator_spi(value.ike_sa_id),
369 value.ike_sa_id->get_responder_spi(value.ike_sa_id),
370 ike_sa);
371 break;
372 case HA_SYNC_CONFIG_NAME:
373 config = find_child_cfg(value.str);
374 break;
375 case HA_SYNC_INBOUND_SPI:
376 inbound_spi = value.u32;
377 break;
378 case HA_SYNC_OUTBOUND_SPI:
379 outbound_spi = value.u32;
380 break;
381 case HA_SYNC_INBOUND_CPI:
382 inbound_cpi = value.u32;
383 break;
384 case HA_SYNC_OUTBOUND_CPI:
385 outbound_cpi = value.u32;
386 break;
387 case HA_SYNC_IPSEC_MODE:
388 mode = value.u8;
389 break;
390 case HA_SYNC_IPCOMP:
391 ipcomp = value.u8;
392 break;
393 case HA_SYNC_ALG_ENCR:
394 encr = value.u16;
395 break;
396 case HA_SYNC_ALG_ENCR_LEN:
397 len = value.u16;
398 break;
399 case HA_SYNC_ALG_INTEG:
400 integ = value.u16;
401 break;
402 case HA_SYNC_NONCE_I:
403 nonce_i = value.chunk;
404 break;
405 case HA_SYNC_NONCE_R:
406 nonce_r = value.chunk;
407 break;
408 case HA_SYNC_SECRET:
409 secret = value.chunk;
410 break;
411 default:
412 break;
413 }
414 }
415 enumerator->destroy(enumerator);
416
417 if (!config)
418 {
419 DBG1(DBG_CHD, "HA sync is missing nodes child configuration");
420 return;
421 }
422 if (!ike_sa)
423 {
424 config->destroy(config);
425 DBG1(DBG_CHD, "IKE_SA for HA sync CHILD_SA not found");
426 return;
427 }
428 charon->bus->set_sa(charon->bus, ike_sa);
429 child_sa = child_sa_create(ike_sa->get_my_host(ike_sa),
430 ike_sa->get_other_host(ike_sa), config, 0,
431 ike_sa->has_condition(ike_sa, COND_NAT_ANY));
432 config->destroy(config);
433 child_sa->set_mode(child_sa, mode);
434 child_sa->set_protocol(child_sa, PROTO_ESP);
435 child_sa->set_ipcomp(child_sa, ipcomp);
436
437 proposal = proposal_create(PROTO_ESP);
438 if (integ)
439 {
440 proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
441 }
442 if (encr)
443 {
444 proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
445 }
446 keymat = ike_sa->get_keymat(ike_sa);
447
448 if (!keymat->derive_child_keys(keymat, proposal, secret.ptr ? &dh : NULL,
449 nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r))
450 {
451 DBG1(DBG_CHD, "HA sync CHILD_SA key derivation failed");
452 child_sa->destroy(child_sa);
453 proposal->destroy(proposal);
454 charon->bus->set_sa(charon->bus, NULL);
455 return;
456 }
457 child_sa->set_proposal(child_sa, proposal);
458 child_sa->set_state(child_sa, CHILD_INSTALLING);
459 proposal->destroy(proposal);
460
461 if (initiator)
462 {
463 if (child_sa->install(child_sa, encr_r, integ_r,
464 inbound_spi, inbound_cpi, TRUE) != SUCCESS ||
465 child_sa->install(child_sa, encr_i, integ_i,
466 outbound_spi, outbound_cpi, FALSE) != SUCCESS)
467 {
468 failed = TRUE;
469 }
470 }
471 else
472 {
473 if (child_sa->install(child_sa, encr_i, integ_i,
474 inbound_spi, inbound_cpi, TRUE) != SUCCESS ||
475 child_sa->install(child_sa, encr_r, integ_r,
476 outbound_spi, outbound_cpi, FALSE) != SUCCESS)
477 {
478 failed = TRUE;
479 }
480 }
481 chunk_clear(&encr_i);
482 chunk_clear(&integ_i);
483 chunk_clear(&encr_r);
484 chunk_clear(&integ_r);
485
486 if (failed)
487 {
488 DBG1(DBG_CHD, "HA sync CHILD_SA installation failed");
489 child_sa->destroy(child_sa);
490 charon->bus->set_sa(charon->bus, NULL);
491 return;
492 }
493
494 /* TODO: Change CHILD_SA API to avoid cloning twice */
495 local_ts = linked_list_create();
496 remote_ts = linked_list_create();
497 enumerator = message->create_attribute_enumerator(message);
498 while (enumerator->enumerate(enumerator, &attribute, &value))
499 {
500 switch (attribute)
501 {
502 case HA_SYNC_LOCAL_TS:
503 local_ts->insert_last(local_ts, value.ts->clone(value.ts));
504 break;
505 case HA_SYNC_REMOTE_TS:
506 remote_ts->insert_last(remote_ts, value.ts->clone(value.ts));
507 break;
508 default:
509 break;
510 }
511 }
512 enumerator->destroy(enumerator);
513 child_sa->add_policies(child_sa, local_ts, remote_ts);
514 local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
515 remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
516
517 child_sa->set_state(child_sa, CHILD_INSTALLED);
518 ike_sa->add_child_sa(ike_sa, child_sa);
519 charon->bus->set_sa(charon->bus, NULL);
520 }
521
522 /**
523 * Process messages of type CHILD_DELETE
524 */
525 static void process_child_delete(private_ha_sync_dispatcher_t *this,
526 ha_sync_message_t *message)
527 {
528 ha_sync_message_attribute_t attribute;
529 ha_sync_message_value_t value;
530 enumerator_t *enumerator;
531 ike_sa_t *ike_sa = NULL;
532
533 enumerator = message->create_attribute_enumerator(message);
534 while (enumerator->enumerate(enumerator, &attribute, &value))
535 {
536 switch (attribute)
537 {
538 case HA_SYNC_IKE_ID:
539 if (this->cache->has_ike_sa(this->cache, value.ike_sa_id))
540 {
541 ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id);
542 DBG2(DBG_CHD, "got HA_SYNC_CHILD_DELETE: %llx:%llx - %p",
543 value.ike_sa_id->get_initiator_spi(value.ike_sa_id),
544 value.ike_sa_id->get_responder_spi(value.ike_sa_id),
545 ike_sa);
546 continue;
547 }
548 break;
549 case HA_SYNC_INBOUND_SPI:
550 if (!ike_sa || ike_sa->destroy_child_sa(ike_sa, PROTO_ESP,
551 value.u32) != SUCCESS)
552 {
553 DBG1(DBG_CHD, "HA sync CHILD_SA 0x%lx delete failed (%p)",
554 value.u32, ike_sa);
555 }
556 break;
557 default:
558 break;
559 }
560 break;
561 }
562 enumerator->destroy(enumerator);
563 }
564
565 /**
566 * Dispatcher job function
567 */
568 static job_requeue_t dispatch(private_ha_sync_dispatcher_t *this)
569 {
570 ha_sync_message_t *message;
571
572 message = this->socket->pull(this->socket);
573 switch (message->get_type(message))
574 {
575 case HA_SYNC_IKE_ADD:
576 process_ike_add(this, message);
577 break;
578 case HA_SYNC_IKE_UPDATE:
579 process_ike_update(this, message);
580 break;
581 case HA_SYNC_IKE_DELETE:
582 process_ike_delete(this, message);
583 break;
584 case HA_SYNC_CHILD_ADD:
585 process_child_add(this, message);
586 break;
587 case HA_SYNC_CHILD_DELETE:
588 process_child_delete(this, message);
589 break;
590 default:
591 DBG1(DBG_CFG, "received unknown HA sync message type %d",
592 message->get_type(message));
593 break;
594 }
595 message->destroy(message);
596
597 return JOB_REQUEUE_DIRECT;
598 }
599
600 /**
601 * Implementation of ha_sync_dispatcher_t.destroy.
602 */
603 static void destroy(private_ha_sync_dispatcher_t *this)
604 {
605 this->job->cancel(this->job);
606 free(this);
607 }
608
609 /**
610 * See header
611 */
612 ha_sync_dispatcher_t *ha_sync_dispatcher_create(ha_sync_socket_t *socket,
613 ha_sync_cache_t *cache)
614 {
615 private_ha_sync_dispatcher_t *this = malloc_thing(private_ha_sync_dispatcher_t);
616
617 this->public.destroy = (void(*)(ha_sync_dispatcher_t*))destroy;
618
619 this->socket = socket;
620 this->cache = cache;
621 this->job = callback_job_create((callback_job_cb_t)dispatch,
622 this, NULL, NULL);
623 charon->processor->queue_job(charon->processor, (job_t*)this->job);
624
625 return &this->public;
626 }