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