d0994a9616f2eb020bd18fcd56566c82d77bd035
[strongswan.git] / src / libcharon / sa / ikev1 / tasks / mode_config.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
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 "mode_config.h"
17
18 #include <daemon.h>
19 #include <encoding/payloads/cp_payload.h>
20
21 typedef struct private_mode_config_t private_mode_config_t;
22
23 /**
24 * Private members of a mode_config_t task.
25 */
26 struct private_mode_config_t {
27
28 /**
29 * Public methods and task_t interface.
30 */
31 mode_config_t public;
32
33 /**
34 * Assigned IKE_SA.
35 */
36 ike_sa_t *ike_sa;
37
38 /**
39 * Are we the initiator?
40 */
41 bool initiator;
42
43 /**
44 * Use pull (CFG_REQUEST/RESPONSE) or push (CFG_SET/ACK)?
45 */
46 bool pull;
47
48 /**
49 * Received list of virtual IPs, host_t*
50 */
51 linked_list_t *vips;
52
53 /**
54 * Requested/received list of attributes, entry_t
55 */
56 linked_list_t *attributes;
57
58 /**
59 * Identifier to include in response
60 */
61 u_int16_t identifier;
62 };
63
64 /**
65 * Entry for a attribute and associated handler
66 */
67 typedef struct {
68 /** attribute type */
69 configuration_attribute_type_t type;
70 /** handler for this attribute */
71 attribute_handler_t *handler;
72 } entry_t;
73
74 /**
75 * build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip
76 */
77 static configuration_attribute_t *build_vip(host_t *vip)
78 {
79 configuration_attribute_type_t type;
80 chunk_t chunk, prefix;
81
82 if (vip->get_family(vip) == AF_INET)
83 {
84 type = INTERNAL_IP4_ADDRESS;
85 if (vip->is_anyaddr(vip))
86 {
87 chunk = chunk_empty;
88 }
89 else
90 {
91 chunk = vip->get_address(vip);
92 }
93 }
94 else
95 {
96 type = INTERNAL_IP6_ADDRESS;
97 if (vip->is_anyaddr(vip))
98 {
99 chunk = chunk_empty;
100 }
101 else
102 {
103 prefix = chunk_alloca(1);
104 *prefix.ptr = 64;
105 chunk = vip->get_address(vip);
106 chunk = chunk_cata("cc", chunk, prefix);
107 }
108 }
109 return configuration_attribute_create_chunk(PLV1_CONFIGURATION_ATTRIBUTE,
110 type, chunk);
111 }
112
113 /**
114 * Handle a received attribute as initiator
115 */
116 static void handle_attribute(private_mode_config_t *this,
117 configuration_attribute_t *ca)
118 {
119 attribute_handler_t *handler = NULL;
120 enumerator_t *enumerator;
121 entry_t *entry;
122
123 /* find the handler which requested this attribute */
124 enumerator = this->attributes->create_enumerator(this->attributes);
125 while (enumerator->enumerate(enumerator, &entry))
126 {
127 if (entry->type == ca->get_type(ca))
128 {
129 handler = entry->handler;
130 this->attributes->remove_at(this->attributes, enumerator);
131 free(entry);
132 break;
133 }
134 }
135 enumerator->destroy(enumerator);
136
137 /* and pass it to the handle function */
138 handler = charon->attributes->handle(charon->attributes,
139 this->ike_sa, handler, ca->get_type(ca), ca->get_chunk(ca));
140 this->ike_sa->add_configuration_attribute(this->ike_sa,
141 handler, ca->get_type(ca), ca->get_chunk(ca));
142 }
143
144 /**
145 * process a single configuration attribute
146 */
147 static void process_attribute(private_mode_config_t *this,
148 configuration_attribute_t *ca)
149 {
150 host_t *ip;
151 chunk_t addr;
152 int family = AF_INET6;
153
154 switch (ca->get_type(ca))
155 {
156 case INTERNAL_IP4_ADDRESS:
157 family = AF_INET;
158 /* fall */
159 case INTERNAL_IP6_ADDRESS:
160 {
161 addr = ca->get_chunk(ca);
162 if (addr.len == 0)
163 {
164 ip = host_create_any(family);
165 }
166 else
167 {
168 /* skip prefix byte in IPv6 payload*/
169 if (family == AF_INET6)
170 {
171 addr.len--;
172 }
173 ip = host_create_from_chunk(family, addr, 0);
174 }
175 if (ip)
176 {
177 this->vips->insert_last(this->vips, ip);
178 }
179 break;
180 }
181 default:
182 {
183 if (this->initiator == this->pull)
184 {
185 handle_attribute(this, ca);
186 }
187 }
188 }
189 }
190
191 /**
192 * Check if config allows push mode when acting as task responder
193 */
194 static bool accept_push(private_mode_config_t *this)
195 {
196 enumerator_t *enumerator;
197 peer_cfg_t *config;
198 bool vip;
199 host_t *host;
200
201 config = this->ike_sa->get_peer_cfg(this->ike_sa);
202 enumerator = config->create_virtual_ip_enumerator(config);
203 vip = enumerator->enumerate(enumerator, &host);
204 enumerator->destroy(enumerator);
205
206 return vip && !config->use_pull_mode(config);
207 }
208
209 /**
210 * Scan for configuration payloads and attributes
211 */
212 static void process_payloads(private_mode_config_t *this, message_t *message)
213 {
214 enumerator_t *enumerator, *attributes;
215 payload_t *payload;
216
217 enumerator = message->create_payload_enumerator(message);
218 while (enumerator->enumerate(enumerator, &payload))
219 {
220 if (payload->get_type(payload) == PLV1_CONFIGURATION)
221 {
222 cp_payload_t *cp = (cp_payload_t*)payload;
223 configuration_attribute_t *ca;
224
225 switch (cp->get_type(cp))
226 {
227 case CFG_SET:
228 /* when acting as a responder, we detect the mode using
229 * the type of configuration payload. But we should double
230 * check the peer is allowed to use push mode on us. */
231 if (!this->initiator && accept_push(this))
232 {
233 this->pull = FALSE;
234 }
235 /* FALL */
236 case CFG_REQUEST:
237 this->identifier = cp->get_identifier(cp);
238 /* FALL */
239 case CFG_REPLY:
240 attributes = cp->create_attribute_enumerator(cp);
241 while (attributes->enumerate(attributes, &ca))
242 {
243 DBG2(DBG_IKE, "processing %N attribute",
244 configuration_attribute_type_names, ca->get_type(ca));
245 process_attribute(this, ca);
246 }
247 attributes->destroy(attributes);
248 break;
249 case CFG_ACK:
250 break;
251 default:
252 DBG1(DBG_IKE, "ignoring %N config payload",
253 config_type_names, cp->get_type(cp));
254 break;
255 }
256 }
257 }
258 enumerator->destroy(enumerator);
259 }
260
261 /**
262 * Add an attribute to a configuration payload, and store it in task
263 */
264 static void add_attribute(private_mode_config_t *this, cp_payload_t *cp,
265 configuration_attribute_type_t type, chunk_t data,
266 attribute_handler_t *handler)
267 {
268 entry_t *entry;
269
270 cp->add_attribute(cp,
271 configuration_attribute_create_chunk(PLV1_CONFIGURATION_ATTRIBUTE,
272 type, data));
273 INIT(entry,
274 .type = type,
275 .handler = handler,
276 );
277 this->attributes->insert_last(this->attributes, entry);
278 }
279
280 /**
281 * Build a CFG_REQUEST as initiator
282 */
283 static status_t build_request(private_mode_config_t *this, message_t *message)
284 {
285 cp_payload_t *cp;
286 enumerator_t *enumerator;
287 attribute_handler_t *handler;
288 peer_cfg_t *config;
289 configuration_attribute_type_t type;
290 chunk_t data;
291 linked_list_t *vips;
292 host_t *host;
293
294 cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_REQUEST);
295
296 vips = linked_list_create();
297
298 /* reuse virtual IP if we already have one */
299 enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
300 while (enumerator->enumerate(enumerator, &host))
301 {
302 vips->insert_last(vips, host);
303 }
304 enumerator->destroy(enumerator);
305
306 if (vips->get_count(vips) == 0)
307 {
308 config = this->ike_sa->get_peer_cfg(this->ike_sa);
309 enumerator = config->create_virtual_ip_enumerator(config);
310 while (enumerator->enumerate(enumerator, &host))
311 {
312 vips->insert_last(vips, host);
313 }
314 enumerator->destroy(enumerator);
315 }
316
317 if (vips->get_count(vips))
318 {
319 enumerator = vips->create_enumerator(vips);
320 while (enumerator->enumerate(enumerator, &host))
321 {
322 cp->add_attribute(cp, build_vip(host));
323 }
324 enumerator->destroy(enumerator);
325 }
326
327 enumerator = charon->attributes->create_initiator_enumerator(
328 charon->attributes, this->ike_sa, vips);
329 while (enumerator->enumerate(enumerator, &handler, &type, &data))
330 {
331 add_attribute(this, cp, type, data, handler);
332 }
333 enumerator->destroy(enumerator);
334
335 vips->destroy(vips);
336
337 message->add_payload(message, (payload_t*)cp);
338
339 return NEED_MORE;
340 }
341
342 /**
343 * Build a CFG_SET as initiator
344 */
345 static status_t build_set(private_mode_config_t *this, message_t *message)
346 {
347 enumerator_t *enumerator;
348 configuration_attribute_type_t type;
349 chunk_t value;
350 cp_payload_t *cp;
351 peer_cfg_t *config;
352 identification_t *id;
353 linked_list_t *pools, *migrated, *vips;
354 host_t *any4, *any6, *found;
355 char *name;
356
357 cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_SET);
358
359 id = this->ike_sa->get_other_eap_id(this->ike_sa);
360 config = this->ike_sa->get_peer_cfg(this->ike_sa);
361
362 /* if we migrated virtual IPs during reauthentication, reassign them */
363 migrated = linked_list_create_from_enumerator(
364 this->ike_sa->create_virtual_ip_enumerator(this->ike_sa,
365 FALSE));
366 vips = migrated->clone_offset(migrated, offsetof(host_t, clone));
367 migrated->destroy(migrated);
368 this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
369
370 /* in push mode, we ask each configured pool for an address */
371 if (!vips->get_count(vips))
372 {
373 any4 = host_create_any(AF_INET);
374 any6 = host_create_any(AF_INET6);
375 enumerator = config->create_pool_enumerator(config);
376 while (enumerator->enumerate(enumerator, &name))
377 {
378 pools = linked_list_create_with_items(name, NULL);
379 /* try IPv4, then IPv6 */
380 found = charon->attributes->acquire_address(charon->attributes,
381 pools, this->ike_sa, any4);
382 if (!found)
383 {
384 found = charon->attributes->acquire_address(charon->attributes,
385 pools, this->ike_sa, any6);
386 }
387 pools->destroy(pools);
388 if (found)
389 {
390 vips->insert_last(vips, found);
391 }
392 }
393 enumerator->destroy(enumerator);
394 any4->destroy(any4);
395 any6->destroy(any6);
396 }
397
398 enumerator = vips->create_enumerator(vips);
399 while (enumerator->enumerate(enumerator, &found))
400 {
401 DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
402 this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
403 cp->add_attribute(cp, build_vip(found));
404 this->vips->insert_last(this->vips, found);
405 vips->remove_at(vips, enumerator);
406 }
407 enumerator->destroy(enumerator);
408 vips->destroy(vips);
409
410 charon->bus->assign_vips(charon->bus, this->ike_sa, TRUE);
411
412 /* query registered providers for additional attributes to include */
413 pools = linked_list_create_from_enumerator(
414 config->create_pool_enumerator(config));
415 enumerator = charon->attributes->create_responder_enumerator(
416 charon->attributes, pools, this->ike_sa, this->vips);
417 while (enumerator->enumerate(enumerator, &type, &value))
418 {
419 add_attribute(this, cp, type, value, NULL);
420 }
421 enumerator->destroy(enumerator);
422 pools->destroy(pools);
423
424 message->add_payload(message, (payload_t*)cp);
425
426 return SUCCESS;
427 }
428
429 METHOD(task_t, build_i, status_t,
430 private_mode_config_t *this, message_t *message)
431 {
432 if (this->pull)
433 {
434 return build_request(this, message);
435 }
436 return build_set(this, message);
437 }
438
439 /**
440 * Store received virtual IPs to the IKE_SA, install them
441 */
442 static void install_vips(private_mode_config_t *this)
443 {
444 enumerator_t *enumerator;
445 host_t *host;
446
447 this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
448
449 enumerator = this->vips->create_enumerator(this->vips);
450 while (enumerator->enumerate(enumerator, &host))
451 {
452 if (!host->is_anyaddr(host))
453 {
454 this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
455 }
456 }
457 enumerator->destroy(enumerator);
458
459 charon->bus->handle_vips(charon->bus, this->ike_sa, TRUE);
460 }
461
462 METHOD(task_t, process_r, status_t,
463 private_mode_config_t *this, message_t *message)
464 {
465 process_payloads(this, message);
466
467 if (!this->pull)
468 {
469 install_vips(this);
470 }
471 return NEED_MORE;
472 }
473
474 /**
475 * Assign a migrated virtual IP
476 */
477 static host_t *assign_migrated_vip(linked_list_t *migrated, host_t *requested)
478 {
479 enumerator_t *enumerator;
480 host_t *found = NULL, *vip;
481
482 enumerator = migrated->create_enumerator(migrated);
483 while (enumerator->enumerate(enumerator, &vip))
484 {
485 if (vip->ip_equals(vip, requested))
486 {
487 migrated->remove_at(migrated, enumerator);
488 found = vip;
489 break;
490 }
491 }
492 enumerator->destroy(enumerator);
493 return found;
494 }
495
496 /**
497 * Build CFG_REPLY message after receiving CFG_REQUEST
498 */
499 static status_t build_reply(private_mode_config_t *this, message_t *message)
500 {
501 enumerator_t *enumerator;
502 configuration_attribute_type_t type;
503 chunk_t value;
504 cp_payload_t *cp;
505 peer_cfg_t *config;
506 identification_t *id;
507 linked_list_t *vips, *pools, *migrated;
508 host_t *requested, *found;
509
510 cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_REPLY);
511
512 id = this->ike_sa->get_other_eap_id(this->ike_sa);
513 config = this->ike_sa->get_peer_cfg(this->ike_sa);
514 pools = linked_list_create_from_enumerator(
515 config->create_pool_enumerator(config));
516 /* if we migrated virtual IPs during reauthentication, reassign them */
517 vips = linked_list_create_from_enumerator(
518 this->ike_sa->create_virtual_ip_enumerator(this->ike_sa,
519 FALSE));
520 migrated = vips->clone_offset(vips, offsetof(host_t, clone));
521 vips->destroy(vips);
522 this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
523
524 vips = linked_list_create();
525 enumerator = this->vips->create_enumerator(this->vips);
526 while (enumerator->enumerate(enumerator, &requested))
527 {
528 DBG1(DBG_IKE, "peer requested virtual IP %H", requested);
529
530 found = assign_migrated_vip(migrated, requested);
531 if (!found)
532 {
533 found = charon->attributes->acquire_address(charon->attributes,
534 pools, this->ike_sa, requested);
535 }
536 if (found)
537 {
538 DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
539 this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
540 cp->add_attribute(cp, build_vip(found));
541 vips->insert_last(vips, found);
542 }
543 else
544 {
545 DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'",
546 requested, id);
547 }
548 }
549 enumerator->destroy(enumerator);
550
551 charon->bus->assign_vips(charon->bus, this->ike_sa, TRUE);
552
553 /* query registered providers for additional attributes to include */
554 enumerator = charon->attributes->create_responder_enumerator(
555 charon->attributes, pools, this->ike_sa, vips);
556 while (enumerator->enumerate(enumerator, &type, &value))
557 {
558 cp->add_attribute(cp,
559 configuration_attribute_create_chunk(PLV1_CONFIGURATION_ATTRIBUTE,
560 type, value));
561 }
562 enumerator->destroy(enumerator);
563 /* if a client did not re-request all adresses, release them */
564 enumerator = migrated->create_enumerator(migrated);
565 while (enumerator->enumerate(enumerator, &found))
566 {
567 charon->attributes->release_address(charon->attributes,
568 pools, found, this->ike_sa);
569 }
570 enumerator->destroy(enumerator);
571 migrated->destroy_offset(migrated, offsetof(host_t, destroy));
572 vips->destroy_offset(vips, offsetof(host_t, destroy));
573 pools->destroy(pools);
574
575 cp->set_identifier(cp, this->identifier);
576 message->add_payload(message, (payload_t*)cp);
577
578 return SUCCESS;
579 }
580
581 /**
582 * Build CFG_ACK for a received CFG_SET
583 */
584 static status_t build_ack(private_mode_config_t *this, message_t *message)
585 {
586 cp_payload_t *cp;
587 enumerator_t *enumerator;
588 host_t *host;
589 configuration_attribute_type_t type;
590 entry_t *entry;
591
592 cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_ACK);
593
594 /* return empty attributes for installed IPs */
595
596 enumerator = this->vips->create_enumerator(this->vips);
597 while (enumerator->enumerate(enumerator, &host))
598 {
599 type = INTERNAL_IP6_ADDRESS;
600 if (host->get_family(host) == AF_INET6)
601 {
602 type = INTERNAL_IP6_ADDRESS;
603 }
604 else
605 {
606 type = INTERNAL_IP4_ADDRESS;
607 }
608 cp->add_attribute(cp, configuration_attribute_create_chunk(
609 PLV1_CONFIGURATION_ATTRIBUTE, type, chunk_empty));
610 }
611 enumerator->destroy(enumerator);
612
613 enumerator = this->attributes->create_enumerator(this->attributes);
614 while (enumerator->enumerate(enumerator, &entry))
615 {
616 cp->add_attribute(cp,
617 configuration_attribute_create_chunk(PLV1_CONFIGURATION_ATTRIBUTE,
618 entry->type, chunk_empty));
619 }
620 enumerator->destroy(enumerator);
621
622 cp->set_identifier(cp, this->identifier);
623 message->add_payload(message, (payload_t*)cp);
624
625 return SUCCESS;
626 }
627
628 METHOD(task_t, build_r, status_t,
629 private_mode_config_t *this, message_t *message)
630 {
631 if (this->pull)
632 {
633 return build_reply(this, message);
634 }
635 return build_ack(this, message);
636 }
637
638 METHOD(task_t, process_i, status_t,
639 private_mode_config_t *this, message_t *message)
640 {
641 process_payloads(this, message);
642
643 if (this->pull)
644 {
645 install_vips(this);
646 }
647 return SUCCESS;
648 }
649
650 METHOD(task_t, get_type, task_type_t,
651 private_mode_config_t *this)
652 {
653 return TASK_MODE_CONFIG;
654 }
655
656 METHOD(task_t, migrate, void,
657 private_mode_config_t *this, ike_sa_t *ike_sa)
658 {
659 this->ike_sa = ike_sa;
660 this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
661 this->vips = linked_list_create();
662 this->attributes->destroy_function(this->attributes, free);
663 this->attributes = linked_list_create();
664 }
665
666 METHOD(task_t, destroy, void,
667 private_mode_config_t *this)
668 {
669 this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
670 this->attributes->destroy_function(this->attributes, free);
671 free(this);
672 }
673
674 /*
675 * Described in header.
676 */
677 mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull)
678 {
679 private_mode_config_t *this;
680
681 INIT(this,
682 .public = {
683 .task = {
684 .get_type = _get_type,
685 .migrate = _migrate,
686 .destroy = _destroy,
687 },
688 },
689 .initiator = initiator,
690 .pull = initiator ? pull : TRUE,
691 .ike_sa = ike_sa,
692 .attributes = linked_list_create(),
693 .vips = linked_list_create(),
694 );
695
696 if (initiator)
697 {
698 this->public.task.build = _build_i;
699 this->public.task.process = _process_i;
700 }
701 else
702 {
703 this->public.task.build = _build_r;
704 this->public.task.process = _process_r;
705 }
706
707 return &this->public;
708 }