child-cfg: Add equals() method
[strongswan.git] / src / libcharon / config / child_cfg.c
1 /*
2 * Copyright (C) 2008-2015 Tobias Brunner
3 * Copyright (C) 2005-2007 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * Hochschule fuer Technik Rapperswil
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 "child_cfg.h"
19
20 #include <stdint.h>
21
22 #include <daemon.h>
23
24 ENUM(action_names, ACTION_NONE, ACTION_RESTART,
25 "clear",
26 "hold",
27 "restart",
28 );
29
30 /** Default replay window size, if not set using charon.replay_window */
31 #define DEFAULT_REPLAY_WINDOW 32
32
33 typedef struct private_child_cfg_t private_child_cfg_t;
34
35 /**
36 * Private data of an child_cfg_t object
37 */
38 struct private_child_cfg_t {
39
40 /**
41 * Public part
42 */
43 child_cfg_t public;
44
45 /**
46 * Number of references hold by others to this child_cfg
47 */
48 refcount_t refcount;
49
50 /**
51 * Name of the child_cfg, used to query it
52 */
53 char *name;
54
55 /**
56 * list for all proposals
57 */
58 linked_list_t *proposals;
59
60 /**
61 * list for traffic selectors for my site
62 */
63 linked_list_t *my_ts;
64
65 /**
66 * list for traffic selectors for others site
67 */
68 linked_list_t *other_ts;
69
70 /**
71 * updown script
72 */
73 char *updown;
74
75 /**
76 * allow host access
77 */
78 bool hostaccess;
79
80 /**
81 * Mode to propose for a initiated CHILD: tunnel/transport
82 */
83 ipsec_mode_t mode;
84
85 /**
86 * action to take to start CHILD_SA
87 */
88 action_t start_action;
89
90 /**
91 * action to take on DPD
92 */
93 action_t dpd_action;
94
95 /**
96 * action to take on CHILD_SA close
97 */
98 action_t close_action;
99
100 /**
101 * CHILD_SA lifetime config
102 */
103 lifetime_cfg_t lifetime;
104
105 /**
106 * enable IPComp
107 */
108 bool use_ipcomp;
109
110 /**
111 * Inactivity timeout
112 */
113 u_int32_t inactivity;
114
115 /**
116 * Reqid to install CHILD_SA with
117 */
118 u_int32_t reqid;
119
120 /**
121 * Optional mark to install inbound CHILD_SA with
122 */
123 mark_t mark_in;
124
125 /**
126 * Optional mark to install outbound CHILD_SA with
127 */
128 mark_t mark_out;
129
130 /**
131 * Traffic Flow Confidentiality padding, if enabled
132 */
133 u_int32_t tfc;
134
135 /**
136 * set up IPsec transport SA in MIPv6 proxy mode
137 */
138 bool proxy_mode;
139
140 /**
141 * enable installation and removal of kernel IPsec policies
142 */
143 bool install_policy;
144
145 /**
146 * anti-replay window size
147 */
148 u_int32_t replay_window;
149 };
150
151 METHOD(child_cfg_t, get_name, char*,
152 private_child_cfg_t *this)
153 {
154 return this->name;
155 }
156
157 METHOD(child_cfg_t, add_proposal, void,
158 private_child_cfg_t *this, proposal_t *proposal)
159 {
160 if (proposal)
161 {
162 this->proposals->insert_last(this->proposals, proposal);
163 }
164 }
165
166 static bool match_proposal(proposal_t *item, proposal_t *proposal)
167 {
168 return item->equals(item, proposal);
169 }
170
171 METHOD(child_cfg_t, get_proposals, linked_list_t*,
172 private_child_cfg_t *this, bool strip_dh)
173 {
174 enumerator_t *enumerator;
175 proposal_t *current;
176 linked_list_t *proposals = linked_list_create();
177
178 enumerator = this->proposals->create_enumerator(this->proposals);
179 while (enumerator->enumerate(enumerator, &current))
180 {
181 current = current->clone(current);
182 if (strip_dh)
183 {
184 current->strip_dh(current, MODP_NONE);
185 }
186 if (proposals->find_first(proposals, (linked_list_match_t)match_proposal,
187 NULL, current) == SUCCESS)
188 {
189 current->destroy(current);
190 continue;
191 }
192 proposals->insert_last(proposals, current);
193 }
194 enumerator->destroy(enumerator);
195
196 DBG2(DBG_CFG, "configured proposals: %#P", proposals);
197
198 return proposals;
199 }
200
201 METHOD(child_cfg_t, select_proposal, proposal_t*,
202 private_child_cfg_t*this, linked_list_t *proposals, bool strip_dh,
203 bool private)
204 {
205 enumerator_t *stored_enum, *supplied_enum;
206 proposal_t *stored, *supplied, *selected = NULL;
207
208 stored_enum = this->proposals->create_enumerator(this->proposals);
209 supplied_enum = proposals->create_enumerator(proposals);
210
211 /* compare all stored proposals with all supplied. Stored ones are preferred. */
212 while (stored_enum->enumerate(stored_enum, &stored))
213 {
214 stored = stored->clone(stored);
215 while (supplied_enum->enumerate(supplied_enum, &supplied))
216 {
217 if (strip_dh)
218 {
219 stored->strip_dh(stored, MODP_NONE);
220 }
221 selected = stored->select(stored, supplied, private);
222 if (selected)
223 {
224 DBG2(DBG_CFG, "received proposals: %#P", proposals);
225 DBG2(DBG_CFG, "configured proposals: %#P", this->proposals);
226 DBG2(DBG_CFG, "selected proposal: %P", selected);
227 break;
228 }
229 }
230 stored->destroy(stored);
231 if (selected)
232 {
233 break;
234 }
235 supplied_enum->destroy(supplied_enum);
236 supplied_enum = proposals->create_enumerator(proposals);
237 }
238 stored_enum->destroy(stored_enum);
239 supplied_enum->destroy(supplied_enum);
240 if (selected == NULL)
241 {
242 DBG1(DBG_CFG, "received proposals: %#P", proposals);
243 DBG1(DBG_CFG, "configured proposals: %#P", this->proposals);
244 }
245 return selected;
246 }
247
248 METHOD(child_cfg_t, add_traffic_selector, void,
249 private_child_cfg_t *this, bool local, traffic_selector_t *ts)
250 {
251 if (local)
252 {
253 this->my_ts->insert_last(this->my_ts, ts);
254 }
255 else
256 {
257 this->other_ts->insert_last(this->other_ts, ts);
258 }
259 }
260
261 METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
262 private_child_cfg_t *this, bool local, linked_list_t *supplied,
263 linked_list_t *hosts)
264 {
265 enumerator_t *e1, *e2;
266 traffic_selector_t *ts1, *ts2, *selected;
267 linked_list_t *result, *derived;
268 host_t *host;
269
270 result = linked_list_create();
271 derived = linked_list_create();
272 if (local)
273 {
274 e1 = this->my_ts->create_enumerator(this->my_ts);
275 }
276 else
277 {
278 e1 = this->other_ts->create_enumerator(this->other_ts);
279 }
280 /* In a first step, replace "dynamic" TS with the host list */
281 while (e1->enumerate(e1, &ts1))
282 {
283 if (hosts && hosts->get_count(hosts) &&
284 ts1->is_dynamic(ts1))
285 {
286 e2 = hosts->create_enumerator(hosts);
287 while (e2->enumerate(e2, &host))
288 {
289 ts2 = ts1->clone(ts1);
290 ts2->set_address(ts2, host);
291 derived->insert_last(derived, ts2);
292 }
293 e2->destroy(e2);
294 }
295 else
296 {
297 derived->insert_last(derived, ts1->clone(ts1));
298 }
299 }
300 e1->destroy(e1);
301
302 DBG2(DBG_CFG, "%s traffic selectors for %s:",
303 supplied ? "selecting" : "proposing", local ? "us" : "other");
304 if (supplied == NULL)
305 {
306 while (derived->remove_first(derived, (void**)&ts1) == SUCCESS)
307 {
308 DBG2(DBG_CFG, " %R", ts1);
309 result->insert_last(result, ts1);
310 }
311 derived->destroy(derived);
312 }
313 else
314 {
315 e1 = derived->create_enumerator(derived);
316 e2 = supplied->create_enumerator(supplied);
317 /* enumerate all configured/derived selectors */
318 while (e1->enumerate(e1, &ts1))
319 {
320 /* enumerate all supplied traffic selectors */
321 while (e2->enumerate(e2, &ts2))
322 {
323 selected = ts1->get_subset(ts1, ts2);
324 if (selected)
325 {
326 DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
327 ts1, ts2, selected);
328 result->insert_last(result, selected);
329 }
330 else
331 {
332 DBG2(DBG_CFG, " config: %R, received: %R => no match",
333 ts1, ts2);
334 }
335 }
336 supplied->reset_enumerator(supplied, e2);
337 }
338 e1->destroy(e1);
339 e2->destroy(e2);
340
341 /* check if we/peer did any narrowing, raise alert */
342 e1 = derived->create_enumerator(derived);
343 e2 = result->create_enumerator(result);
344 while (e1->enumerate(e1, &ts1))
345 {
346 if (!e2->enumerate(e2, &ts2) || !ts1->equals(ts1, ts2))
347 {
348 charon->bus->alert(charon->bus, ALERT_TS_NARROWED,
349 local, result, this);
350 break;
351 }
352 }
353 e1->destroy(e1);
354 e2->destroy(e2);
355
356 derived->destroy_offset(derived, offsetof(traffic_selector_t, destroy));
357 }
358
359 /* remove any redundant traffic selectors in the list */
360 e1 = result->create_enumerator(result);
361 e2 = result->create_enumerator(result);
362 while (e1->enumerate(e1, &ts1))
363 {
364 while (e2->enumerate(e2, &ts2))
365 {
366 if (ts1 != ts2)
367 {
368 if (ts2->is_contained_in(ts2, ts1))
369 {
370 result->remove_at(result, e2);
371 ts2->destroy(ts2);
372 result->reset_enumerator(result, e1);
373 break;
374 }
375 if (ts1->is_contained_in(ts1, ts2))
376 {
377 result->remove_at(result, e1);
378 ts1->destroy(ts1);
379 break;
380 }
381 }
382 }
383 result->reset_enumerator(result, e2);
384 }
385 e1->destroy(e1);
386 e2->destroy(e2);
387
388 return result;
389 }
390
391 METHOD(child_cfg_t, get_updown, char*,
392 private_child_cfg_t *this)
393 {
394 return this->updown;
395 }
396
397 METHOD(child_cfg_t, get_hostaccess, bool,
398 private_child_cfg_t *this)
399 {
400 return this->hostaccess;
401 }
402
403 /**
404 * Applies jitter to the rekey value. Returns the new rekey value.
405 * Note: The distribution of random values is not perfect, but it
406 * should get the job done.
407 */
408 static u_int64_t apply_jitter(u_int64_t rekey, u_int64_t jitter)
409 {
410 if (jitter == 0)
411 {
412 return rekey;
413 }
414 jitter = (jitter == UINT64_MAX) ? jitter : jitter + 1;
415 return rekey - jitter * (random() / (RAND_MAX + 1.0));
416 }
417 #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
418
419 METHOD(child_cfg_t, get_lifetime, lifetime_cfg_t*,
420 private_child_cfg_t *this)
421 {
422 lifetime_cfg_t *lft = malloc_thing(lifetime_cfg_t);
423 memcpy(lft, &this->lifetime, sizeof(lifetime_cfg_t));
424 APPLY_JITTER(lft->time);
425 APPLY_JITTER(lft->bytes);
426 APPLY_JITTER(lft->packets);
427 return lft;
428 }
429
430 METHOD(child_cfg_t, get_mode, ipsec_mode_t,
431 private_child_cfg_t *this)
432 {
433 return this->mode;
434 }
435
436 METHOD(child_cfg_t, get_start_action, action_t,
437 private_child_cfg_t *this)
438 {
439 return this->start_action;
440 }
441
442 METHOD(child_cfg_t, get_dpd_action, action_t,
443 private_child_cfg_t *this)
444 {
445 return this->dpd_action;
446 }
447
448 METHOD(child_cfg_t, get_close_action, action_t,
449 private_child_cfg_t *this)
450 {
451 return this->close_action;
452 }
453
454 METHOD(child_cfg_t, get_dh_group, diffie_hellman_group_t,
455 private_child_cfg_t *this)
456 {
457 enumerator_t *enumerator;
458 proposal_t *proposal;
459 u_int16_t dh_group = MODP_NONE;
460
461 enumerator = this->proposals->create_enumerator(this->proposals);
462 while (enumerator->enumerate(enumerator, &proposal))
463 {
464 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
465 {
466 break;
467 }
468 }
469 enumerator->destroy(enumerator);
470 return dh_group;
471 }
472
473 METHOD(child_cfg_t, use_ipcomp, bool,
474 private_child_cfg_t *this)
475 {
476 return this->use_ipcomp;
477 }
478
479 METHOD(child_cfg_t, get_inactivity, u_int32_t,
480 private_child_cfg_t *this)
481 {
482 return this->inactivity;
483 }
484
485 METHOD(child_cfg_t, get_reqid, u_int32_t,
486 private_child_cfg_t *this)
487 {
488 return this->reqid;
489 }
490
491 METHOD(child_cfg_t, get_mark, mark_t,
492 private_child_cfg_t *this, bool inbound)
493 {
494 return inbound ? this->mark_in : this->mark_out;
495 }
496
497 METHOD(child_cfg_t, get_tfc, u_int32_t,
498 private_child_cfg_t *this)
499 {
500 return this->tfc;
501 }
502
503 METHOD(child_cfg_t, get_replay_window, u_int32_t,
504 private_child_cfg_t *this)
505 {
506 return this->replay_window;
507 }
508
509 METHOD(child_cfg_t, set_replay_window, void,
510 private_child_cfg_t *this, u_int32_t replay_window)
511 {
512 this->replay_window = replay_window;
513 }
514
515 METHOD(child_cfg_t, set_mipv6_options, void,
516 private_child_cfg_t *this, bool proxy_mode, bool install_policy)
517 {
518 this->proxy_mode = proxy_mode;
519 this->install_policy = install_policy;
520 }
521
522 METHOD(child_cfg_t, use_proxy_mode, bool,
523 private_child_cfg_t *this)
524 {
525 return this->proxy_mode;
526 }
527
528 METHOD(child_cfg_t, install_policy, bool,
529 private_child_cfg_t *this)
530 {
531 return this->install_policy;
532 }
533
534 #define LT_PART_EQUALS(a, b) ({ a.life == b.life && a.rekey == b.rekey && a.jitter == b.jitter; })
535 #define LIFETIME_EQUALS(a, b) ({ LT_PART_EQUALS(a.time, b.time) && LT_PART_EQUALS(a.bytes, b.bytes) && LT_PART_EQUALS(a.packets, b.packets); })
536
537 METHOD(child_cfg_t, equals, bool,
538 private_child_cfg_t *this, child_cfg_t *other_pub)
539 {
540 private_child_cfg_t *other = (private_child_cfg_t*)other_pub;
541
542 if (this == other)
543 {
544 return TRUE;
545 }
546 if (this->public.equals != other->public.equals)
547 {
548 return FALSE;
549 }
550 if (!this->proposals->equals_offset(this->proposals, other->proposals,
551 offsetof(proposal_t, equals)))
552 {
553 return FALSE;
554 }
555 if (!this->my_ts->equals_offset(this->my_ts, other->my_ts,
556 offsetof(traffic_selector_t, equals)))
557 {
558 return FALSE;
559 }
560 if (!this->other_ts->equals_offset(this->other_ts, other->other_ts,
561 offsetof(traffic_selector_t, equals)))
562 {
563 return FALSE;
564 }
565 return this->hostaccess == other->hostaccess &&
566 this->mode == other->mode &&
567 this->start_action == other->start_action &&
568 this->dpd_action == other->dpd_action &&
569 this->close_action == other->close_action &&
570 LIFETIME_EQUALS(this->lifetime, other->lifetime) &&
571 this->use_ipcomp == other->use_ipcomp &&
572 this->inactivity == other->inactivity &&
573 this->reqid == other->reqid &&
574 this->mark_in.value == other->mark_in.value &&
575 this->mark_in.mask == other->mark_in.mask &&
576 this->mark_out.value == other->mark_out.value &&
577 this->mark_out.mask == other->mark_out.mask &&
578 this->tfc == other->tfc &&
579 this->replay_window == other->replay_window &&
580 this->proxy_mode == other->proxy_mode &&
581 this->install_policy == other->install_policy &&
582 streq(this->updown, other->updown);
583 }
584
585 METHOD(child_cfg_t, get_ref, child_cfg_t*,
586 private_child_cfg_t *this)
587 {
588 ref_get(&this->refcount);
589 return &this->public;
590 }
591
592 METHOD(child_cfg_t, destroy, void,
593 private_child_cfg_t *this)
594 {
595 if (ref_put(&this->refcount))
596 {
597 this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
598 this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
599 this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
600 if (this->updown)
601 {
602 free(this->updown);
603 }
604 free(this->name);
605 free(this);
606 }
607 }
608
609 /*
610 * Described in header-file
611 */
612 child_cfg_t *child_cfg_create(char *name, lifetime_cfg_t *lifetime,
613 char *updown, bool hostaccess,
614 ipsec_mode_t mode, action_t start_action,
615 action_t dpd_action, action_t close_action,
616 bool ipcomp, u_int32_t inactivity, u_int32_t reqid,
617 mark_t *mark_in, mark_t *mark_out, u_int32_t tfc)
618 {
619 private_child_cfg_t *this;
620
621 INIT(this,
622 .public = {
623 .get_name = _get_name,
624 .add_traffic_selector = _add_traffic_selector,
625 .get_traffic_selectors = _get_traffic_selectors,
626 .add_proposal = _add_proposal,
627 .get_proposals = _get_proposals,
628 .select_proposal = _select_proposal,
629 .get_updown = _get_updown,
630 .get_hostaccess = _get_hostaccess,
631 .get_mode = _get_mode,
632 .get_start_action = _get_start_action,
633 .get_dpd_action = _get_dpd_action,
634 .get_close_action = _get_close_action,
635 .get_lifetime = _get_lifetime,
636 .get_dh_group = _get_dh_group,
637 .set_mipv6_options = _set_mipv6_options,
638 .use_ipcomp = _use_ipcomp,
639 .get_inactivity = _get_inactivity,
640 .get_reqid = _get_reqid,
641 .get_mark = _get_mark,
642 .get_tfc = _get_tfc,
643 .get_replay_window = _get_replay_window,
644 .set_replay_window = _set_replay_window,
645 .use_proxy_mode = _use_proxy_mode,
646 .install_policy = _install_policy,
647 .equals = _equals,
648 .get_ref = _get_ref,
649 .destroy = _destroy,
650 },
651 .name = strdup(name),
652 .updown = strdupnull(updown),
653 .hostaccess = hostaccess,
654 .mode = mode,
655 .start_action = start_action,
656 .dpd_action = dpd_action,
657 .close_action = close_action,
658 .use_ipcomp = ipcomp,
659 .inactivity = inactivity,
660 .reqid = reqid,
661 .proxy_mode = FALSE,
662 .install_policy = TRUE,
663 .refcount = 1,
664 .proposals = linked_list_create(),
665 .my_ts = linked_list_create(),
666 .other_ts = linked_list_create(),
667 .tfc = tfc,
668 .replay_window = lib->settings->get_int(lib->settings,
669 "%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
670 );
671
672 if (mark_in)
673 {
674 this->mark_in = *mark_in;
675 }
676 if (mark_out)
677 {
678 this->mark_out = *mark_out;
679 }
680 memcpy(&this->lifetime, lifetime, sizeof(lifetime_cfg_t));
681
682 return &this->public;
683 }