]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2016-2024 Tobias Brunner | |
3 | * Copyright (C) 2008 Martin Willi | |
4 | * | |
5 | * Copyright (C) secunet Security Networks AG | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
16 | */ | |
17 | ||
18 | #include "ha_dispatcher.h" | |
19 | ||
20 | #include <daemon.h> | |
21 | #include <sa/ikev2/keymat_v2.h> | |
22 | #include <sa/ikev1/keymat_v1.h> | |
23 | #include <processing/jobs/callback_job.h> | |
24 | #include <processing/jobs/adopt_children_job.h> | |
25 | ||
26 | typedef struct private_ha_dispatcher_t private_ha_dispatcher_t; | |
27 | typedef struct ha_key_exchange_t ha_key_exchange_t; | |
28 | ||
29 | /** | |
30 | * Private data of an ha_dispatcher_t object. | |
31 | */ | |
32 | struct private_ha_dispatcher_t { | |
33 | ||
34 | /** | |
35 | * Public ha_dispatcher_t interface. | |
36 | */ | |
37 | ha_dispatcher_t public; | |
38 | ||
39 | /** | |
40 | * socket to pull messages from | |
41 | */ | |
42 | ha_socket_t *socket; | |
43 | ||
44 | /** | |
45 | * segments to control | |
46 | */ | |
47 | ha_segments_t *segments; | |
48 | ||
49 | /** | |
50 | * Cache for resync | |
51 | */ | |
52 | ha_cache_t *cache; | |
53 | ||
54 | /** | |
55 | * Kernel helper | |
56 | */ | |
57 | ha_kernel_t *kernel; | |
58 | ||
59 | /** | |
60 | * HA enabled pool | |
61 | */ | |
62 | ha_attribute_t *attr; | |
63 | }; | |
64 | ||
65 | /** | |
66 | * KE implementation for HA synced KE shared secrets | |
67 | */ | |
68 | struct ha_key_exchange_t { | |
69 | ||
70 | /** | |
71 | * Public interface | |
72 | */ | |
73 | key_exchange_t ke; | |
74 | ||
75 | /** | |
76 | * Shared secret | |
77 | */ | |
78 | chunk_t secret; | |
79 | ||
80 | /** | |
81 | * Own public value (IKEv1 only) | |
82 | */ | |
83 | chunk_t pub; | |
84 | }; | |
85 | ||
86 | METHOD(key_exchange_t, ke_get_shared_secret, bool, | |
87 | ha_key_exchange_t *this, chunk_t *secret) | |
88 | { | |
89 | *secret = chunk_clone(this->secret); | |
90 | return TRUE; | |
91 | } | |
92 | ||
93 | METHOD(key_exchange_t, ke_get_public_key, bool, | |
94 | ha_key_exchange_t *this, chunk_t *value) | |
95 | { | |
96 | *value = chunk_clone(this->pub); | |
97 | return TRUE; | |
98 | } | |
99 | ||
100 | METHOD(key_exchange_t, ke_destroy, void, | |
101 | ha_key_exchange_t *this) | |
102 | { | |
103 | free(this); | |
104 | } | |
105 | ||
106 | /** | |
107 | * Create a HA synced KE implementation | |
108 | */ | |
109 | static key_exchange_t *ha_key_exchange_create(chunk_t secret, chunk_t pub) | |
110 | { | |
111 | ha_key_exchange_t *this; | |
112 | ||
113 | INIT(this, | |
114 | .ke = { | |
115 | .get_shared_secret = _ke_get_shared_secret, | |
116 | .get_public_key = _ke_get_public_key, | |
117 | .destroy = _ke_destroy, | |
118 | }, | |
119 | .secret = secret, | |
120 | .pub = pub, | |
121 | ); | |
122 | ||
123 | return &this->ke; | |
124 | } | |
125 | ||
126 | /** | |
127 | * Add the given KE methods to a proposal | |
128 | */ | |
129 | static void add_ke_methods_to_proposal(proposal_t *proposal, uint16_t ke_alg, | |
130 | chunk_t add_kes) | |
131 | { | |
132 | int i, count; | |
133 | ||
134 | if (ke_alg) | |
135 | { | |
136 | proposal->add_algorithm(proposal, KEY_EXCHANGE_METHOD, ke_alg, 0); | |
137 | } | |
138 | count = min(add_kes.len / sizeof(uint16_t), MAX_ADDITIONAL_KEY_EXCHANGES); | |
139 | for (i = 0; i < count; i++) | |
140 | { | |
141 | ke_alg = ntohs(((uint16_t*)add_kes.ptr)[i]); | |
142 | if (ke_alg) | |
143 | { | |
144 | proposal->add_algorithm(proposal, i + ADDITIONAL_KEY_EXCHANGE_1, | |
145 | ke_alg, 0); | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
150 | /** | |
151 | * Process messages of type IKE_ADD | |
152 | */ | |
153 | static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message) | |
154 | { | |
155 | ha_message_attribute_t attribute; | |
156 | ha_message_value_t value; | |
157 | enumerator_t *enumerator; | |
158 | ike_sa_t *ike_sa = NULL, *old_sa = NULL; | |
159 | ike_version_t version = IKEV2; | |
160 | uint16_t encr = 0, len = 0, integ = 0, prf = 0, old_prf = PRF_UNDEFINED; | |
161 | uint16_t ke_alg = 0; | |
162 | chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty; | |
163 | chunk_t secret = chunk_empty, old_skd = chunk_empty; | |
164 | chunk_t add_secret = chunk_empty, add_kes = chunk_empty; | |
165 | chunk_t dh_local = chunk_empty, dh_remote = chunk_empty, psk = chunk_empty; | |
166 | host_t *other = NULL; | |
167 | bool ok = FALSE; | |
168 | auth_method_t method = AUTH_RSA; | |
169 | ||
170 | enumerator = message->create_attribute_enumerator(message); | |
171 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
172 | { | |
173 | switch (attribute) | |
174 | { | |
175 | case HA_IKE_ID: | |
176 | ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, | |
177 | value.ike_sa_id); | |
178 | if (!ike_sa) | |
179 | { | |
180 | ike_sa = ike_sa_create(value.ike_sa_id, | |
181 | value.ike_sa_id->is_initiator(value.ike_sa_id), version); | |
182 | } | |
183 | break; | |
184 | case HA_IKE_REKEY_ID: | |
185 | old_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, | |
186 | value.ike_sa_id); | |
187 | break; | |
188 | case HA_REMOTE_ADDR: | |
189 | other = value.host->clone(value.host); | |
190 | break; | |
191 | case HA_IKE_VERSION: | |
192 | version = value.u8; | |
193 | break; | |
194 | case HA_NONCE_I: | |
195 | nonce_i = value.chunk; | |
196 | break; | |
197 | case HA_NONCE_R: | |
198 | nonce_r = value.chunk; | |
199 | break; | |
200 | case HA_SECRET: | |
201 | secret = value.chunk; | |
202 | break; | |
203 | case HA_ADD_SECRET: | |
204 | add_secret = value.chunk; | |
205 | break; | |
206 | case HA_LOCAL_DH: | |
207 | dh_local = value.chunk; | |
208 | break; | |
209 | case HA_REMOTE_DH: | |
210 | dh_remote = value.chunk; | |
211 | break; | |
212 | case HA_PSK: | |
213 | psk = value.chunk; | |
214 | break; | |
215 | case HA_OLD_SKD: | |
216 | old_skd = value.chunk; | |
217 | break; | |
218 | case HA_ALG_ENCR: | |
219 | encr = value.u16; | |
220 | break; | |
221 | case HA_ALG_ENCR_LEN: | |
222 | len = value.u16; | |
223 | break; | |
224 | case HA_ALG_INTEG: | |
225 | integ = value.u16; | |
226 | break; | |
227 | case HA_ALG_PRF: | |
228 | prf = value.u16; | |
229 | break; | |
230 | case HA_ALG_OLD_PRF: | |
231 | old_prf = value.u16; | |
232 | break; | |
233 | case HA_ALG_KE: | |
234 | ke_alg = value.u16; | |
235 | break; | |
236 | case HA_ALG_ADD_KES: | |
237 | add_kes = value.chunk; | |
238 | break; | |
239 | case HA_AUTH_METHOD: | |
240 | method = value.u16; | |
241 | break; | |
242 | default: | |
243 | break; | |
244 | } | |
245 | } | |
246 | enumerator->destroy(enumerator); | |
247 | ||
248 | if (ike_sa) | |
249 | { | |
250 | proposal_t *proposal; | |
251 | key_exchange_t *ke; | |
252 | array_t *kes = NULL; | |
253 | bool key_update = FALSE; | |
254 | ||
255 | proposal = ike_sa->get_proposal(ike_sa); | |
256 | if (!proposal) | |
257 | { | |
258 | proposal = proposal_create(PROTO_IKE, 0); | |
259 | if (integ) | |
260 | { | |
261 | proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0); | |
262 | } | |
263 | if (encr) | |
264 | { | |
265 | proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); | |
266 | } | |
267 | if (prf) | |
268 | { | |
269 | proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0); | |
270 | } | |
271 | add_ke_methods_to_proposal(proposal, ke_alg, add_kes); | |
272 | } | |
273 | else | |
274 | { | |
275 | key_update = TRUE; | |
276 | } | |
277 | charon->bus->set_sa(charon->bus, ike_sa); | |
278 | ke = ha_key_exchange_create(secret, dh_local); | |
279 | array_insert_create(&kes, ARRAY_HEAD, ke); | |
280 | if (ike_sa->get_version(ike_sa) == IKEV2) | |
281 | { | |
282 | keymat_v2_t *keymat_v2 = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); | |
283 | ||
284 | if (add_secret.len) | |
285 | { | |
286 | ke = ha_key_exchange_create(add_secret, chunk_empty); | |
287 | array_insert_create(&kes, ARRAY_TAIL, ke); | |
288 | } | |
289 | if (key_update) | |
290 | { | |
291 | old_prf = keymat_v2->get_skd(keymat_v2, &old_skd); | |
292 | } | |
293 | ok = keymat_v2->derive_ike_keys(keymat_v2, proposal, kes, nonce_i, | |
294 | nonce_r, ike_sa->get_id(ike_sa), old_prf, old_skd); | |
295 | } | |
296 | else if (ike_sa->get_version(ike_sa) == IKEV1) | |
297 | { | |
298 | keymat_v1_t *keymat_v1 = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); | |
299 | shared_key_t *shared = NULL; | |
300 | ||
301 | if (psk.len) | |
302 | { | |
303 | method = AUTH_PSK; | |
304 | shared = shared_key_create(SHARED_IKE, chunk_clone(psk)); | |
305 | } | |
306 | if (keymat_v1->create_hasher(keymat_v1, proposal)) | |
307 | { | |
308 | ok = keymat_v1->derive_ike_keys(keymat_v1, proposal, | |
309 | ke, dh_remote, nonce_i, nonce_r, | |
310 | ike_sa->get_id(ike_sa), method, shared); | |
311 | } | |
312 | DESTROY_IF(shared); | |
313 | } | |
314 | array_destroy_offset(kes, offsetof(key_exchange_t, destroy)); | |
315 | if (ok) | |
316 | { | |
317 | if (old_sa) | |
318 | { /* register IKE_SA before calling inherit_post() so no scheduled | |
319 | * jobs are lost */ | |
320 | charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, | |
321 | ike_sa); | |
322 | ike_sa->inherit_pre(ike_sa, old_sa); | |
323 | ike_sa->inherit_post(ike_sa, old_sa); | |
324 | charon->ike_sa_manager->checkin_and_destroy( | |
325 | charon->ike_sa_manager, old_sa); | |
326 | old_sa = NULL; | |
327 | } | |
328 | if (other) | |
329 | { | |
330 | ike_sa->set_other_host(ike_sa, other); | |
331 | other = NULL; | |
332 | } | |
333 | if (!key_update) | |
334 | { | |
335 | ike_sa->set_state(ike_sa, IKE_CONNECTING); | |
336 | ike_sa->set_proposal(ike_sa, proposal); | |
337 | } | |
338 | this->cache->cache(this->cache, ike_sa, message); | |
339 | message = NULL; | |
340 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
341 | } | |
342 | else | |
343 | { | |
344 | DBG1(DBG_IKE, "HA keymat derivation failed"); | |
345 | if (key_update) | |
346 | { | |
347 | charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, | |
348 | ike_sa); | |
349 | } | |
350 | else | |
351 | { | |
352 | ike_sa->destroy(ike_sa); | |
353 | charon->bus->set_sa(charon->bus, NULL); | |
354 | } | |
355 | } | |
356 | if (!key_update) | |
357 | { | |
358 | proposal->destroy(proposal); | |
359 | } | |
360 | } | |
361 | if (old_sa) | |
362 | { | |
363 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, old_sa); | |
364 | } | |
365 | DESTROY_IF(other); | |
366 | DESTROY_IF(message); | |
367 | } | |
368 | ||
369 | /** | |
370 | * Apply all set conditions to the IKE_SA | |
371 | */ | |
372 | static void set_conditions(ike_sa_t *ike_sa, ike_condition_t conditions) | |
373 | { | |
374 | ike_condition_t i, private = (conditions & COND_PRIVATE_MARKER); | |
375 | ||
376 | for (i = 0; i < (sizeof(i) * 8) - 1; ++i) | |
377 | { | |
378 | ike_condition_t cond = (1 << i) | private; | |
379 | ||
380 | ike_sa->set_condition(ike_sa, cond, (conditions & cond) == cond); | |
381 | } | |
382 | } | |
383 | ||
384 | /** | |
385 | * Apply all enabled extensions to the IKE_SA | |
386 | */ | |
387 | static void set_extensions(ike_sa_t *ike_sa, ike_extension_t extensions) | |
388 | { | |
389 | ike_extension_t i, private = (extensions & EXT_PRIVATE_MARKER); | |
390 | ||
391 | for (i = 0; i < (sizeof(i) * 8) - 1; ++i) | |
392 | { | |
393 | ike_extension_t ext = (1 << i) | private; | |
394 | ||
395 | if ((extensions & ext) == ext) | |
396 | { | |
397 | ike_sa->enable_extension(ike_sa, ext); | |
398 | } | |
399 | } | |
400 | } | |
401 | ||
402 | /** | |
403 | * Process messages of type IKE_UPDATE | |
404 | */ | |
405 | static void process_ike_update(private_ha_dispatcher_t *this, | |
406 | ha_message_t *message) | |
407 | { | |
408 | ha_message_attribute_t attribute; | |
409 | ha_message_value_t value; | |
410 | enumerator_t *enumerator; | |
411 | ike_sa_t *ike_sa = NULL; | |
412 | peer_cfg_t *peer_cfg = NULL; | |
413 | auth_cfg_t *auth; | |
414 | bool received_vip = FALSE, first_local_vip = TRUE, first_peer_addr = TRUE; | |
415 | ||
416 | enumerator = message->create_attribute_enumerator(message); | |
417 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
418 | { | |
419 | if (attribute != HA_IKE_ID && ike_sa == NULL) | |
420 | { | |
421 | /* must be first attribute */ | |
422 | break; | |
423 | } | |
424 | switch (attribute) | |
425 | { | |
426 | case HA_IKE_ID: | |
427 | ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, | |
428 | value.ike_sa_id); | |
429 | break; | |
430 | case HA_LOCAL_ID: | |
431 | ike_sa->set_my_id(ike_sa, value.id->clone(value.id)); | |
432 | break; | |
433 | case HA_REMOTE_ID: | |
434 | ike_sa->set_other_id(ike_sa, value.id->clone(value.id)); | |
435 | break; | |
436 | case HA_REMOTE_EAP_ID: | |
437 | auth = auth_cfg_create(); | |
438 | auth->add(auth, AUTH_RULE_EAP_IDENTITY, value.id->clone(value.id)); | |
439 | ike_sa->add_auth_cfg(ike_sa, FALSE, auth); | |
440 | break; | |
441 | case HA_LOCAL_ADDR: | |
442 | ike_sa->set_my_host(ike_sa, value.host->clone(value.host)); | |
443 | break; | |
444 | case HA_REMOTE_ADDR: | |
445 | ike_sa->set_other_host(ike_sa, value.host->clone(value.host)); | |
446 | break; | |
447 | case HA_LOCAL_VIP: | |
448 | if (first_local_vip) | |
449 | { | |
450 | ike_sa->clear_virtual_ips(ike_sa, TRUE); | |
451 | first_local_vip = FALSE; | |
452 | } | |
453 | ike_sa->add_virtual_ip(ike_sa, TRUE, value.host); | |
454 | break; | |
455 | case HA_REMOTE_VIP: | |
456 | if (!received_vip) | |
457 | { | |
458 | ike_sa->clear_virtual_ips(ike_sa, FALSE); | |
459 | } | |
460 | ike_sa->add_virtual_ip(ike_sa, FALSE, value.host); | |
461 | received_vip = TRUE; | |
462 | break; | |
463 | case HA_PEER_ADDR: | |
464 | if (first_peer_addr) | |
465 | { | |
466 | ike_sa->clear_peer_addresses(ike_sa); | |
467 | first_peer_addr = FALSE; | |
468 | } | |
469 | ike_sa->add_peer_address(ike_sa, value.host->clone(value.host)); | |
470 | break; | |
471 | case HA_CONFIG_NAME: | |
472 | peer_cfg = charon->backends->get_peer_cfg_by_name( | |
473 | charon->backends, value.str); | |
474 | if (peer_cfg) | |
475 | { | |
476 | ike_sa->set_peer_cfg(ike_sa, peer_cfg); | |
477 | peer_cfg->destroy(peer_cfg); | |
478 | } | |
479 | else | |
480 | { | |
481 | DBG1(DBG_IKE, "HA is missing nodes peer configuration"); | |
482 | charon->ike_sa_manager->checkin_and_destroy( | |
483 | charon->ike_sa_manager, ike_sa); | |
484 | ike_sa = NULL; | |
485 | } | |
486 | break; | |
487 | case HA_EXTENSIONS: | |
488 | set_extensions(ike_sa, value.u32); | |
489 | break; | |
490 | case HA_CONDITIONS: | |
491 | set_conditions(ike_sa, value.u32); | |
492 | break; | |
493 | default: | |
494 | break; | |
495 | } | |
496 | } | |
497 | enumerator->destroy(enumerator); | |
498 | ||
499 | if (ike_sa) | |
500 | { | |
501 | if (ike_sa->get_state(ike_sa) == IKE_CONNECTING && | |
502 | ike_sa->get_peer_cfg(ike_sa)) | |
503 | { | |
504 | DBG1(DBG_CFG, "installed HA passive IKE_SA '%s' %H[%Y]...%H[%Y]", | |
505 | ike_sa->get_name(ike_sa), | |
506 | ike_sa->get_my_host(ike_sa), ike_sa->get_my_id(ike_sa), | |
507 | ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa)); | |
508 | ike_sa->set_state(ike_sa, IKE_PASSIVE); | |
509 | } | |
510 | if (received_vip) | |
511 | { | |
512 | enumerator_t *pools, *vips; | |
513 | host_t *vip; | |
514 | char *pool; | |
515 | ||
516 | peer_cfg = ike_sa->get_peer_cfg(ike_sa); | |
517 | if (peer_cfg) | |
518 | { | |
519 | pools = peer_cfg->create_pool_enumerator(peer_cfg); | |
520 | while (pools->enumerate(pools, &pool)) | |
521 | { | |
522 | vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); | |
523 | while (vips->enumerate(vips, &vip)) | |
524 | { | |
525 | this->attr->reserve(this->attr, pool, vip); | |
526 | } | |
527 | vips->destroy(vips); | |
528 | } | |
529 | pools->destroy(pools); | |
530 | } | |
531 | } | |
532 | #ifdef USE_IKEV1 | |
533 | if (ike_sa->get_version(ike_sa) == IKEV1) | |
534 | { | |
535 | lib->processor->queue_job(lib->processor, (job_t*) | |
536 | adopt_children_job_create(ike_sa->get_id(ike_sa))); | |
537 | } | |
538 | #endif /* USE_IKEV1 */ | |
539 | this->cache->cache(this->cache, ike_sa, message); | |
540 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
541 | } | |
542 | else | |
543 | { | |
544 | DBG1(DBG_CFG, "passive HA IKE_SA to update not found"); | |
545 | message->destroy(message); | |
546 | } | |
547 | } | |
548 | ||
549 | /** | |
550 | * Process messages of type IKE_MID_INITIATOR/RESPONDER | |
551 | */ | |
552 | static void process_ike_mid(private_ha_dispatcher_t *this, | |
553 | ha_message_t *message, bool initiator) | |
554 | { | |
555 | ha_message_attribute_t attribute; | |
556 | ha_message_value_t value; | |
557 | enumerator_t *enumerator; | |
558 | ike_sa_t *ike_sa = NULL; | |
559 | uint32_t mid = 0; | |
560 | ||
561 | enumerator = message->create_attribute_enumerator(message); | |
562 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
563 | { | |
564 | switch (attribute) | |
565 | { | |
566 | case HA_IKE_ID: | |
567 | ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, | |
568 | value.ike_sa_id); | |
569 | break; | |
570 | case HA_MID: | |
571 | mid = value.u32; | |
572 | break; | |
573 | default: | |
574 | break; | |
575 | } | |
576 | } | |
577 | enumerator->destroy(enumerator); | |
578 | ||
579 | if (ike_sa) | |
580 | { | |
581 | if (mid) | |
582 | { | |
583 | ike_sa->set_message_id(ike_sa, initiator, mid); | |
584 | } | |
585 | this->cache->cache(this->cache, ike_sa, message); | |
586 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
587 | } | |
588 | else | |
589 | { | |
590 | message->destroy(message); | |
591 | } | |
592 | } | |
593 | ||
594 | /** | |
595 | * Process messages of type IKE_IV | |
596 | */ | |
597 | static void process_ike_iv(private_ha_dispatcher_t *this, ha_message_t *message) | |
598 | { | |
599 | ha_message_attribute_t attribute; | |
600 | ha_message_value_t value; | |
601 | enumerator_t *enumerator; | |
602 | ike_sa_t *ike_sa = NULL; | |
603 | chunk_t iv = chunk_empty; | |
604 | ||
605 | enumerator = message->create_attribute_enumerator(message); | |
606 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
607 | { | |
608 | switch (attribute) | |
609 | { | |
610 | case HA_IKE_ID: | |
611 | ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, | |
612 | value.ike_sa_id); | |
613 | break; | |
614 | case HA_IV: | |
615 | iv = value.chunk; | |
616 | break; | |
617 | default: | |
618 | break; | |
619 | } | |
620 | } | |
621 | enumerator->destroy(enumerator); | |
622 | ||
623 | if (ike_sa) | |
624 | { | |
625 | if (ike_sa->get_version(ike_sa) == IKEV1) | |
626 | { | |
627 | if (iv.len) | |
628 | { | |
629 | keymat_v1_t *keymat; | |
630 | ||
631 | keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); | |
632 | if (keymat->update_iv(keymat, 0, iv)) | |
633 | { | |
634 | keymat->confirm_iv(keymat, 0); | |
635 | } | |
636 | } | |
637 | } | |
638 | this->cache->cache(this->cache, ike_sa, message); | |
639 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
640 | } | |
641 | else | |
642 | { | |
643 | message->destroy(message); | |
644 | } | |
645 | } | |
646 | ||
647 | /** | |
648 | * Process messages of type IKE_DELETE | |
649 | */ | |
650 | static void process_ike_delete(private_ha_dispatcher_t *this, | |
651 | ha_message_t *message) | |
652 | { | |
653 | ha_message_attribute_t attribute; | |
654 | ha_message_value_t value; | |
655 | enumerator_t *enumerator; | |
656 | ike_sa_t *ike_sa = NULL; | |
657 | ||
658 | enumerator = message->create_attribute_enumerator(message); | |
659 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
660 | { | |
661 | switch (attribute) | |
662 | { | |
663 | case HA_IKE_ID: | |
664 | ike_sa = charon->ike_sa_manager->checkout( | |
665 | charon->ike_sa_manager, value.ike_sa_id); | |
666 | break; | |
667 | default: | |
668 | break; | |
669 | } | |
670 | } | |
671 | enumerator->destroy(enumerator); | |
672 | if (ike_sa) | |
673 | { | |
674 | this->cache->cache(this->cache, ike_sa, message); | |
675 | charon->ike_sa_manager->checkin_and_destroy( | |
676 | charon->ike_sa_manager, ike_sa); | |
677 | } | |
678 | else | |
679 | { | |
680 | message->destroy(message); | |
681 | } | |
682 | } | |
683 | ||
684 | /** | |
685 | * Lookup a child cfg from the peer cfg by name | |
686 | */ | |
687 | static child_cfg_t* find_child_cfg(ike_sa_t *ike_sa, char *name) | |
688 | { | |
689 | peer_cfg_t *peer_cfg; | |
690 | child_cfg_t *current, *found = NULL; | |
691 | enumerator_t *enumerator; | |
692 | ||
693 | peer_cfg = ike_sa->get_peer_cfg(ike_sa); | |
694 | if (peer_cfg) | |
695 | { | |
696 | enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); | |
697 | while (enumerator->enumerate(enumerator, ¤t)) | |
698 | { | |
699 | if (streq(current->get_name(current), name)) | |
700 | { | |
701 | found = current; | |
702 | break; | |
703 | } | |
704 | } | |
705 | enumerator->destroy(enumerator); | |
706 | } | |
707 | return found; | |
708 | } | |
709 | ||
710 | /** | |
711 | * Process messages of type CHILD_ADD | |
712 | */ | |
713 | static void process_child_add(private_ha_dispatcher_t *this, | |
714 | ha_message_t *message) | |
715 | { | |
716 | ha_message_attribute_t attribute; | |
717 | ha_message_value_t value; | |
718 | enumerator_t *enumerator; | |
719 | ike_sa_t *ike_sa = NULL; | |
720 | char *config_name = ""; | |
721 | child_cfg_t *config = NULL; | |
722 | child_sa_t *child_sa; | |
723 | proposal_t *proposal; | |
724 | bool initiator = FALSE, failed = FALSE, ok = FALSE; | |
725 | uint32_t inbound_spi = 0, outbound_spi = 0; | |
726 | uint16_t inbound_cpi = 0, outbound_cpi = 0; | |
727 | uint8_t mode = MODE_TUNNEL, ipcomp = 0; | |
728 | uint16_t encr = 0, integ = 0, len = 0, ke_alg = 0; | |
729 | uint16_t esn = NO_EXT_SEQ_NUMBERS; | |
730 | chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty; | |
731 | chunk_t add_secret = chunk_empty, add_kes = chunk_empty; | |
732 | chunk_t encr_i, integ_i, encr_r, integ_r; | |
733 | linked_list_t *local_ts, *remote_ts; | |
734 | key_exchange_t *ke = NULL; | |
735 | array_t *kes = NULL; | |
736 | ||
737 | enumerator = message->create_attribute_enumerator(message); | |
738 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
739 | { | |
740 | switch (attribute) | |
741 | { | |
742 | case HA_IKE_ID: | |
743 | ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, | |
744 | value.ike_sa_id); | |
745 | break; | |
746 | case HA_CONFIG_NAME: | |
747 | config_name = value.str; | |
748 | break; | |
749 | case HA_INITIATOR: | |
750 | initiator = value.u8; | |
751 | break; | |
752 | case HA_INBOUND_SPI: | |
753 | inbound_spi = value.u32; | |
754 | break; | |
755 | case HA_OUTBOUND_SPI: | |
756 | outbound_spi = value.u32; | |
757 | break; | |
758 | case HA_INBOUND_CPI: | |
759 | inbound_cpi = value.u32; | |
760 | break; | |
761 | case HA_OUTBOUND_CPI: | |
762 | outbound_cpi = value.u32; | |
763 | break; | |
764 | case HA_IPSEC_MODE: | |
765 | mode = value.u8; | |
766 | break; | |
767 | case HA_IPCOMP: | |
768 | ipcomp = value.u8; | |
769 | break; | |
770 | case HA_ALG_ENCR: | |
771 | encr = value.u16; | |
772 | break; | |
773 | case HA_ALG_ENCR_LEN: | |
774 | len = value.u16; | |
775 | break; | |
776 | case HA_ALG_INTEG: | |
777 | integ = value.u16; | |
778 | break; | |
779 | case HA_ALG_KE: | |
780 | ke_alg = value.u16; | |
781 | break; | |
782 | case HA_ALG_ADD_KES: | |
783 | add_kes = value.chunk; | |
784 | break; | |
785 | case HA_ESN: | |
786 | esn = value.u16; | |
787 | break; | |
788 | case HA_NONCE_I: | |
789 | nonce_i = value.chunk; | |
790 | break; | |
791 | case HA_NONCE_R: | |
792 | nonce_r = value.chunk; | |
793 | break; | |
794 | case HA_SECRET: | |
795 | secret = value.chunk; | |
796 | break; | |
797 | case HA_ADD_SECRET: | |
798 | add_secret = value.chunk; | |
799 | break; | |
800 | default: | |
801 | break; | |
802 | } | |
803 | } | |
804 | enumerator->destroy(enumerator); | |
805 | ||
806 | if (!ike_sa) | |
807 | { | |
808 | DBG1(DBG_CHD, "IKE_SA for HA CHILD_SA not found"); | |
809 | message->destroy(message); | |
810 | return; | |
811 | } | |
812 | config = find_child_cfg(ike_sa, config_name); | |
813 | if (!config) | |
814 | { | |
815 | DBG1(DBG_CHD, "HA is missing nodes child configuration"); | |
816 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
817 | message->destroy(message); | |
818 | return; | |
819 | } | |
820 | ||
821 | child_sa_create_t data = { | |
822 | .encap = ike_sa->has_condition(ike_sa, COND_NAT_ANY), | |
823 | .cpu = CPU_ID_MAX, | |
824 | }; | |
825 | child_sa = child_sa_create(ike_sa->get_my_host(ike_sa), | |
826 | ike_sa->get_other_host(ike_sa), config, &data); | |
827 | child_sa->set_mode(child_sa, mode); | |
828 | child_sa->set_protocol(child_sa, PROTO_ESP); | |
829 | child_sa->set_ipcomp(child_sa, ipcomp); | |
830 | ||
831 | proposal = proposal_create(PROTO_ESP, 0); | |
832 | if (integ) | |
833 | { | |
834 | proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0); | |
835 | } | |
836 | if (encr) | |
837 | { | |
838 | proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); | |
839 | } | |
840 | add_ke_methods_to_proposal(proposal, ke_alg, add_kes); | |
841 | proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, esn, 0); | |
842 | ||
843 | if (secret.len) | |
844 | { | |
845 | ke = ha_key_exchange_create(secret, chunk_empty); | |
846 | array_insert_create(&kes, ARRAY_HEAD, ke); | |
847 | } | |
848 | if (add_secret.len) | |
849 | { | |
850 | ke = ha_key_exchange_create(add_secret, chunk_empty); | |
851 | array_insert_create(&kes, ARRAY_TAIL, ke); | |
852 | } | |
853 | if (ike_sa->get_version(ike_sa) == IKEV2) | |
854 | { | |
855 | keymat_v2_t *keymat_v2 = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); | |
856 | ||
857 | ok = keymat_v2->derive_child_keys(keymat_v2, proposal, kes, | |
858 | nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r); | |
859 | } | |
860 | else if (ike_sa->get_version(ike_sa) == IKEV1) | |
861 | { | |
862 | keymat_v1_t *keymat_v1 = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); | |
863 | uint32_t spi_i, spi_r; | |
864 | ||
865 | spi_i = initiator ? inbound_spi : outbound_spi; | |
866 | spi_r = initiator ? outbound_spi : inbound_spi; | |
867 | ||
868 | ok = keymat_v1->derive_child_keys(keymat_v1, proposal, ke, spi_i, spi_r, | |
869 | nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r); | |
870 | } | |
871 | array_destroy_offset(kes, offsetof(key_exchange_t, destroy)); | |
872 | if (!ok) | |
873 | { | |
874 | DBG1(DBG_CHD, "HA CHILD_SA key derivation failed"); | |
875 | child_sa->destroy(child_sa); | |
876 | proposal->destroy(proposal); | |
877 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
878 | return; | |
879 | } | |
880 | child_sa->set_proposal(child_sa, proposal); | |
881 | child_sa->set_state(child_sa, CHILD_INSTALLING); | |
882 | proposal->destroy(proposal); | |
883 | ||
884 | /* TODO: Change CHILD_SA API to avoid cloning twice */ | |
885 | local_ts = linked_list_create(); | |
886 | remote_ts = linked_list_create(); | |
887 | enumerator = message->create_attribute_enumerator(message); | |
888 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
889 | { | |
890 | switch (attribute) | |
891 | { | |
892 | case HA_LOCAL_TS: | |
893 | local_ts->insert_last(local_ts, value.ts->clone(value.ts)); | |
894 | break; | |
895 | case HA_REMOTE_TS: | |
896 | remote_ts->insert_last(remote_ts, value.ts->clone(value.ts)); | |
897 | break; | |
898 | default: | |
899 | break; | |
900 | } | |
901 | } | |
902 | enumerator->destroy(enumerator); | |
903 | ||
904 | child_sa->set_policies(child_sa, local_ts, remote_ts); | |
905 | ||
906 | if (initiator) | |
907 | { | |
908 | if (child_sa->install(child_sa, encr_r, integ_r, inbound_spi, | |
909 | inbound_cpi, initiator, TRUE, TRUE) != SUCCESS || | |
910 | child_sa->install(child_sa, encr_i, integ_i, outbound_spi, | |
911 | outbound_cpi, initiator, FALSE, TRUE) != SUCCESS) | |
912 | { | |
913 | failed = TRUE; | |
914 | } | |
915 | } | |
916 | else | |
917 | { | |
918 | if (child_sa->install(child_sa, encr_i, integ_i, inbound_spi, | |
919 | inbound_cpi, initiator, TRUE, TRUE) != SUCCESS || | |
920 | child_sa->install(child_sa, encr_r, integ_r, outbound_spi, | |
921 | outbound_cpi, initiator, FALSE, TRUE) != SUCCESS) | |
922 | { | |
923 | failed = TRUE; | |
924 | } | |
925 | } | |
926 | chunk_clear(&encr_i); | |
927 | chunk_clear(&integ_i); | |
928 | chunk_clear(&encr_r); | |
929 | chunk_clear(&integ_r); | |
930 | ||
931 | if (failed) | |
932 | { | |
933 | DBG1(DBG_CHD, "HA CHILD_SA installation failed"); | |
934 | child_sa->destroy(child_sa); | |
935 | local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy)); | |
936 | remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy)); | |
937 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
938 | message->destroy(message); | |
939 | return; | |
940 | } | |
941 | ||
942 | #if DEBUG_LEVEL >= 1 | |
943 | u_int seg_i, seg_o; | |
944 | ||
945 | seg_i = this->kernel->get_segment_spi(this->kernel, | |
946 | ike_sa->get_my_host(ike_sa), inbound_spi); | |
947 | seg_o = this->kernel->get_segment_spi(this->kernel, | |
948 | ike_sa->get_other_host(ike_sa), outbound_spi); | |
949 | DBG1(DBG_CFG, "installed HA CHILD_SA %s{%d} %#R === %#R " | |
950 | "(segment in: %d%s, out: %d%s)", child_sa->get_name(child_sa), | |
951 | child_sa->get_unique_id(child_sa), local_ts, remote_ts, | |
952 | seg_i, this->segments->is_active(this->segments, seg_i) ? "*" : "", | |
953 | seg_o, this->segments->is_active(this->segments, seg_o) ? "*" : ""); | |
954 | #endif /* DEBUG_LEVEL */ | |
955 | ||
956 | child_sa->install_policies(child_sa); | |
957 | local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy)); | |
958 | remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy)); | |
959 | ||
960 | child_sa->set_state(child_sa, CHILD_INSTALLED); | |
961 | ike_sa->add_child_sa(ike_sa, child_sa); | |
962 | message->destroy(message); | |
963 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
964 | } | |
965 | ||
966 | /** | |
967 | * Process messages of type CHILD_DELETE | |
968 | */ | |
969 | static void process_child_delete(private_ha_dispatcher_t *this, | |
970 | ha_message_t *message) | |
971 | { | |
972 | ha_message_attribute_t attribute; | |
973 | ha_message_value_t value; | |
974 | enumerator_t *enumerator; | |
975 | ike_sa_t *ike_sa = NULL; | |
976 | child_sa_t *child_sa; | |
977 | uint32_t spi = 0; | |
978 | ||
979 | enumerator = message->create_attribute_enumerator(message); | |
980 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
981 | { | |
982 | switch (attribute) | |
983 | { | |
984 | case HA_IKE_ID: | |
985 | ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, | |
986 | value.ike_sa_id); | |
987 | break; | |
988 | case HA_INBOUND_SPI: | |
989 | spi = value.u32; | |
990 | break; | |
991 | default: | |
992 | break; | |
993 | } | |
994 | } | |
995 | enumerator->destroy(enumerator); | |
996 | ||
997 | if (ike_sa) | |
998 | { | |
999 | child_sa = ike_sa->get_child_sa(ike_sa, PROTO_ESP, spi, TRUE); | |
1000 | if (child_sa) | |
1001 | { | |
1002 | ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, spi); | |
1003 | } | |
1004 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
1005 | } | |
1006 | message->destroy(message); | |
1007 | } | |
1008 | ||
1009 | /** | |
1010 | * Process messages of type SEGMENT_TAKE/DROP | |
1011 | */ | |
1012 | static void process_segment(private_ha_dispatcher_t *this, | |
1013 | ha_message_t *message, bool take) | |
1014 | { | |
1015 | ha_message_attribute_t attribute; | |
1016 | ha_message_value_t value; | |
1017 | enumerator_t *enumerator; | |
1018 | ||
1019 | enumerator = message->create_attribute_enumerator(message); | |
1020 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
1021 | { | |
1022 | switch (attribute) | |
1023 | { | |
1024 | case HA_SEGMENT: | |
1025 | if (take) | |
1026 | { | |
1027 | DBG1(DBG_CFG, "remote node takes segment %d", value.u16); | |
1028 | this->segments->deactivate(this->segments, value.u16, FALSE); | |
1029 | } | |
1030 | else | |
1031 | { | |
1032 | DBG1(DBG_CFG, "remote node drops segment %d", value.u16); | |
1033 | this->segments->activate(this->segments, value.u16, FALSE); | |
1034 | } | |
1035 | break; | |
1036 | default: | |
1037 | break; | |
1038 | } | |
1039 | } | |
1040 | enumerator->destroy(enumerator); | |
1041 | message->destroy(message); | |
1042 | } | |
1043 | ||
1044 | /** | |
1045 | * Process messages of type STATUS | |
1046 | */ | |
1047 | static void process_status(private_ha_dispatcher_t *this, | |
1048 | ha_message_t *message) | |
1049 | { | |
1050 | ha_message_attribute_t attribute; | |
1051 | ha_message_value_t value; | |
1052 | enumerator_t *enumerator; | |
1053 | segment_mask_t mask = 0; | |
1054 | ||
1055 | enumerator = message->create_attribute_enumerator(message); | |
1056 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
1057 | { | |
1058 | switch (attribute) | |
1059 | { | |
1060 | case HA_SEGMENT: | |
1061 | mask |= SEGMENTS_BIT(value.u16); | |
1062 | break; | |
1063 | default: | |
1064 | break; | |
1065 | } | |
1066 | } | |
1067 | enumerator->destroy(enumerator); | |
1068 | ||
1069 | this->segments->handle_status(this->segments, mask); | |
1070 | message->destroy(message); | |
1071 | } | |
1072 | ||
1073 | /** | |
1074 | * Process messages of type RESYNC | |
1075 | */ | |
1076 | static void process_resync(private_ha_dispatcher_t *this, | |
1077 | ha_message_t *message) | |
1078 | { | |
1079 | ha_message_attribute_t attribute; | |
1080 | ha_message_value_t value; | |
1081 | enumerator_t *enumerator; | |
1082 | ||
1083 | enumerator = message->create_attribute_enumerator(message); | |
1084 | while (enumerator->enumerate(enumerator, &attribute, &value)) | |
1085 | { | |
1086 | switch (attribute) | |
1087 | { | |
1088 | case HA_SEGMENT: | |
1089 | this->cache->resync(this->cache, value.u16); | |
1090 | break; | |
1091 | default: | |
1092 | break; | |
1093 | } | |
1094 | } | |
1095 | enumerator->destroy(enumerator); | |
1096 | message->destroy(message); | |
1097 | } | |
1098 | ||
1099 | /** | |
1100 | * Dispatcher job function | |
1101 | */ | |
1102 | static job_requeue_t dispatch(private_ha_dispatcher_t *this) | |
1103 | { | |
1104 | ha_message_t *message; | |
1105 | ha_message_type_t type; | |
1106 | ||
1107 | message = this->socket->pull(this->socket); | |
1108 | type = message->get_type(message); | |
1109 | if (type != HA_STATUS) | |
1110 | { | |
1111 | DBG2(DBG_CFG, "received HA %N message", ha_message_type_names, | |
1112 | message->get_type(message)); | |
1113 | } | |
1114 | switch (type) | |
1115 | { | |
1116 | case HA_IKE_ADD: | |
1117 | process_ike_add(this, message); | |
1118 | break; | |
1119 | case HA_IKE_UPDATE: | |
1120 | process_ike_update(this, message); | |
1121 | break; | |
1122 | case HA_IKE_MID_INITIATOR: | |
1123 | process_ike_mid(this, message, TRUE); | |
1124 | break; | |
1125 | case HA_IKE_MID_RESPONDER: | |
1126 | process_ike_mid(this, message, FALSE); | |
1127 | break; | |
1128 | case HA_IKE_IV: | |
1129 | process_ike_iv(this, message); | |
1130 | break; | |
1131 | case HA_IKE_DELETE: | |
1132 | process_ike_delete(this, message); | |
1133 | break; | |
1134 | case HA_CHILD_ADD: | |
1135 | process_child_add(this, message); | |
1136 | break; | |
1137 | case HA_CHILD_DELETE: | |
1138 | process_child_delete(this, message); | |
1139 | break; | |
1140 | case HA_SEGMENT_DROP: | |
1141 | process_segment(this, message, FALSE); | |
1142 | break; | |
1143 | case HA_SEGMENT_TAKE: | |
1144 | process_segment(this, message, TRUE); | |
1145 | break; | |
1146 | case HA_STATUS: | |
1147 | process_status(this, message); | |
1148 | break; | |
1149 | case HA_RESYNC: | |
1150 | process_resync(this, message); | |
1151 | break; | |
1152 | default: | |
1153 | DBG1(DBG_CFG, "received unknown HA message type %d", type); | |
1154 | message->destroy(message); | |
1155 | break; | |
1156 | } | |
1157 | return JOB_REQUEUE_DIRECT; | |
1158 | } | |
1159 | ||
1160 | METHOD(ha_dispatcher_t, destroy, void, | |
1161 | private_ha_dispatcher_t *this) | |
1162 | { | |
1163 | free(this); | |
1164 | } | |
1165 | ||
1166 | /** | |
1167 | * See header | |
1168 | */ | |
1169 | ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket, | |
1170 | ha_segments_t *segments, ha_cache_t *cache, | |
1171 | ha_kernel_t *kernel, ha_attribute_t *attr) | |
1172 | { | |
1173 | private_ha_dispatcher_t *this; | |
1174 | ||
1175 | ||
1176 | INIT(this, | |
1177 | .public = { | |
1178 | .destroy = _destroy, | |
1179 | }, | |
1180 | .socket = socket, | |
1181 | .segments = segments, | |
1182 | .cache = cache, | |
1183 | .kernel = kernel, | |
1184 | .attr = attr, | |
1185 | ); | |
1186 | lib->processor->queue_job(lib->processor, | |
1187 | (job_t*)callback_job_create_with_prio((callback_job_cb_t)dispatch, this, | |
1188 | NULL, callback_job_cancel_thread, JOB_PRIO_CRITICAL)); | |
1189 | ||
1190 | return &this->public; | |
1191 | } |