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