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