proposal: Add selection flags to clone() method
[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 proposal_selection_flag_t flags = 0;
213 linked_list_t *proposals = linked_list_create();
214
215 if (strip_dh)
216 {
217 flags |= PROPOSAL_SKIP_DH;
218 }
219
220 enumerator = this->proposals->create_enumerator(this->proposals);
221 while (enumerator->enumerate(enumerator, &current))
222 {
223 current = current->clone(current, flags);
224 if (proposals->find_first(proposals, match_proposal, NULL, current))
225 {
226 current->destroy(current);
227 continue;
228 }
229 proposals->insert_last(proposals, current);
230 }
231 enumerator->destroy(enumerator);
232
233 DBG2(DBG_CFG, "configured proposals: %#P", proposals);
234
235 return proposals;
236 }
237
238 METHOD(child_cfg_t, select_proposal, proposal_t*,
239 private_child_cfg_t*this, linked_list_t *proposals,
240 proposal_selection_flag_t flags)
241 {
242 return proposal_select(this->proposals, proposals, flags);
243 }
244
245 METHOD(child_cfg_t, add_traffic_selector, void,
246 private_child_cfg_t *this, bool local, traffic_selector_t *ts)
247 {
248 if (local)
249 {
250 this->my_ts->insert_last(this->my_ts, ts);
251 }
252 else
253 {
254 this->other_ts->insert_last(this->other_ts, ts);
255 }
256 }
257
258 METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
259 private_child_cfg_t *this, bool local, linked_list_t *supplied,
260 linked_list_t *hosts, bool log)
261 {
262 enumerator_t *e1, *e2;
263 traffic_selector_t *ts1, *ts2, *selected;
264 linked_list_t *result, *derived;
265 host_t *host;
266
267 result = linked_list_create();
268 derived = linked_list_create();
269 if (local)
270 {
271 e1 = this->my_ts->create_enumerator(this->my_ts);
272 }
273 else
274 {
275 e1 = this->other_ts->create_enumerator(this->other_ts);
276 }
277 /* in a first step, replace "dynamic" TS with the host list */
278 while (e1->enumerate(e1, &ts1))
279 {
280 if (hosts && hosts->get_count(hosts))
281 { /* set hosts if TS is dynamic or as initiator in transport mode */
282 bool dynamic = ts1->is_dynamic(ts1),
283 proxy_mode = has_option(this, OPT_PROXY_MODE);
284 if (dynamic || (this->mode == MODE_TRANSPORT && !proxy_mode &&
285 !supplied))
286 {
287 e2 = hosts->create_enumerator(hosts);
288 while (e2->enumerate(e2, &host))
289 {
290 ts2 = ts1->clone(ts1);
291 if (dynamic || !host->is_anyaddr(host))
292 { /* don't make regular TS larger than they were */
293 ts2->set_address(ts2, host);
294 }
295 derived->insert_last(derived, ts2);
296 }
297 e2->destroy(e2);
298 continue;
299 }
300 }
301 derived->insert_last(derived, ts1->clone(ts1));
302 }
303 e1->destroy(e1);
304
305 if (log)
306 {
307 DBG2(DBG_CFG, "%s traffic selectors for %s:",
308 supplied ? "selecting" : "proposing", local ? "us" : "other");
309 }
310 if (!supplied)
311 {
312 while (derived->remove_first(derived, (void**)&ts1) == SUCCESS)
313 {
314 if (log)
315 {
316 DBG2(DBG_CFG, " %R", ts1);
317 }
318 result->insert_last(result, ts1);
319 }
320 derived->destroy(derived);
321 }
322 else
323 {
324 e1 = derived->create_enumerator(derived);
325 e2 = supplied->create_enumerator(supplied);
326 /* enumerate all configured/derived selectors */
327 while (e1->enumerate(e1, &ts1))
328 {
329 /* enumerate all supplied traffic selectors */
330 while (e2->enumerate(e2, &ts2))
331 {
332 selected = ts1->get_subset(ts1, ts2);
333 if (selected)
334 {
335 if (log)
336 {
337 DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
338 ts1, ts2, selected);
339 }
340 result->insert_last(result, selected);
341 }
342 else if (log)
343 {
344 DBG2(DBG_CFG, " config: %R, received: %R => no match",
345 ts1, ts2);
346 }
347 }
348 supplied->reset_enumerator(supplied, e2);
349 }
350 e1->destroy(e1);
351 e2->destroy(e2);
352
353 /* check if we/peer did any narrowing, raise alert */
354 e1 = derived->create_enumerator(derived);
355 e2 = result->create_enumerator(result);
356 while (e1->enumerate(e1, &ts1))
357 {
358 if (!e2->enumerate(e2, &ts2) || !ts1->equals(ts1, ts2))
359 {
360 charon->bus->alert(charon->bus, ALERT_TS_NARROWED,
361 local, result, this);
362 break;
363 }
364 }
365 e1->destroy(e1);
366 e2->destroy(e2);
367
368 derived->destroy_offset(derived, offsetof(traffic_selector_t, destroy));
369 }
370
371 /* remove any redundant traffic selectors in the list */
372 e1 = result->create_enumerator(result);
373 e2 = result->create_enumerator(result);
374 while (e1->enumerate(e1, &ts1))
375 {
376 while (e2->enumerate(e2, &ts2))
377 {
378 if (ts1 != ts2)
379 {
380 if (ts2->is_contained_in(ts2, ts1))
381 {
382 result->remove_at(result, e2);
383 ts2->destroy(ts2);
384 result->reset_enumerator(result, e1);
385 break;
386 }
387 if (ts1->is_contained_in(ts1, ts2))
388 {
389 result->remove_at(result, e1);
390 ts1->destroy(ts1);
391 break;
392 }
393 }
394 }
395 result->reset_enumerator(result, e2);
396 }
397 e1->destroy(e1);
398 e2->destroy(e2);
399
400 return result;
401 }
402
403 METHOD(child_cfg_t, get_updown, char*,
404 private_child_cfg_t *this)
405 {
406 return this->updown;
407 }
408
409 /**
410 * Applies jitter to the rekey value. Returns the new rekey value.
411 * Note: The distribution of random values is not perfect, but it
412 * should get the job done.
413 */
414 static uint64_t apply_jitter(uint64_t rekey, uint64_t jitter)
415 {
416 if (jitter == 0)
417 {
418 return rekey;
419 }
420 jitter = (jitter == UINT64_MAX) ? jitter : jitter + 1;
421 return rekey - jitter * (random() / (RAND_MAX + 1.0));
422 }
423 #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
424
425 METHOD(child_cfg_t, get_lifetime, lifetime_cfg_t*,
426 private_child_cfg_t *this, bool jitter)
427 {
428 lifetime_cfg_t *lft = malloc_thing(lifetime_cfg_t);
429 memcpy(lft, &this->lifetime, sizeof(lifetime_cfg_t));
430 if (!jitter)
431 {
432 lft->time.jitter = lft->bytes.jitter = lft->packets.jitter = 0;
433 }
434 APPLY_JITTER(lft->time);
435 APPLY_JITTER(lft->bytes);
436 APPLY_JITTER(lft->packets);
437 return lft;
438 }
439
440 METHOD(child_cfg_t, get_mode, ipsec_mode_t,
441 private_child_cfg_t *this)
442 {
443 return this->mode;
444 }
445
446 METHOD(child_cfg_t, get_start_action, action_t,
447 private_child_cfg_t *this)
448 {
449 return this->start_action;
450 }
451
452 METHOD(child_cfg_t, get_hw_offload, hw_offload_t,
453 private_child_cfg_t *this)
454 {
455 return this->hw_offload;
456 }
457
458 METHOD(child_cfg_t, get_copy_dscp, dscp_copy_t,
459 private_child_cfg_t *this)
460 {
461 return this->copy_dscp;
462 }
463
464 METHOD(child_cfg_t, get_dpd_action, action_t,
465 private_child_cfg_t *this)
466 {
467 return this->dpd_action;
468 }
469
470 METHOD(child_cfg_t, get_close_action, action_t,
471 private_child_cfg_t *this)
472 {
473 return this->close_action;
474 }
475
476 METHOD(child_cfg_t, get_dh_group, diffie_hellman_group_t,
477 private_child_cfg_t *this)
478 {
479 enumerator_t *enumerator;
480 proposal_t *proposal;
481 uint16_t dh_group = MODP_NONE;
482
483 enumerator = this->proposals->create_enumerator(this->proposals);
484 while (enumerator->enumerate(enumerator, &proposal))
485 {
486 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
487 {
488 break;
489 }
490 }
491 enumerator->destroy(enumerator);
492 return dh_group;
493 }
494
495 METHOD(child_cfg_t, get_inactivity, uint32_t,
496 private_child_cfg_t *this)
497 {
498 return this->inactivity;
499 }
500
501 METHOD(child_cfg_t, get_reqid, uint32_t,
502 private_child_cfg_t *this)
503 {
504 return this->reqid;
505 }
506
507 METHOD(child_cfg_t, get_if_id, uint32_t,
508 private_child_cfg_t *this, bool inbound)
509 {
510 return inbound ? this->if_id_in : this->if_id_out;
511 }
512
513 METHOD(child_cfg_t, get_mark, mark_t,
514 private_child_cfg_t *this, bool inbound)
515 {
516 return inbound ? this->mark_in : this->mark_out;
517 }
518
519 METHOD(child_cfg_t, get_set_mark, mark_t,
520 private_child_cfg_t *this, bool inbound)
521 {
522 return inbound ? this->set_mark_in : this->set_mark_out;
523 }
524
525 METHOD(child_cfg_t, get_tfc, uint32_t,
526 private_child_cfg_t *this)
527 {
528 return this->tfc;
529 }
530
531 METHOD(child_cfg_t, get_manual_prio, uint32_t,
532 private_child_cfg_t *this)
533 {
534 return this->manual_prio;
535 }
536
537 METHOD(child_cfg_t, get_interface, char*,
538 private_child_cfg_t *this)
539 {
540 return this->interface;
541 }
542
543 METHOD(child_cfg_t, get_replay_window, uint32_t,
544 private_child_cfg_t *this)
545 {
546 return this->replay_window;
547 }
548
549 METHOD(child_cfg_t, set_replay_window, void,
550 private_child_cfg_t *this, uint32_t replay_window)
551 {
552 this->replay_window = replay_window;
553 }
554
555 #define LT_PART_EQUALS(a, b) ({ a.life == b.life && a.rekey == b.rekey && a.jitter == b.jitter; })
556 #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); })
557
558 METHOD(child_cfg_t, equals, bool,
559 private_child_cfg_t *this, child_cfg_t *other_pub)
560 {
561 private_child_cfg_t *other = (private_child_cfg_t*)other_pub;
562
563 if (this == other)
564 {
565 return TRUE;
566 }
567 if (this->public.equals != other->public.equals)
568 {
569 return FALSE;
570 }
571 if (!this->proposals->equals_offset(this->proposals, other->proposals,
572 offsetof(proposal_t, equals)))
573 {
574 return FALSE;
575 }
576 if (!this->my_ts->equals_offset(this->my_ts, other->my_ts,
577 offsetof(traffic_selector_t, equals)))
578 {
579 return FALSE;
580 }
581 if (!this->other_ts->equals_offset(this->other_ts, other->other_ts,
582 offsetof(traffic_selector_t, equals)))
583 {
584 return FALSE;
585 }
586 return this->options == other->options &&
587 this->mode == other->mode &&
588 this->start_action == other->start_action &&
589 this->dpd_action == other->dpd_action &&
590 this->close_action == other->close_action &&
591 LIFETIME_EQUALS(this->lifetime, other->lifetime) &&
592 this->inactivity == other->inactivity &&
593 this->reqid == other->reqid &&
594 this->if_id_in == other->if_id_in &&
595 this->if_id_out == other->if_id_out &&
596 this->mark_in.value == other->mark_in.value &&
597 this->mark_in.mask == other->mark_in.mask &&
598 this->mark_out.value == other->mark_out.value &&
599 this->mark_out.mask == other->mark_out.mask &&
600 this->set_mark_in.value == other->set_mark_in.value &&
601 this->set_mark_in.mask == other->set_mark_in.mask &&
602 this->set_mark_out.value == other->set_mark_out.value &&
603 this->set_mark_out.mask == other->set_mark_out.mask &&
604 this->tfc == other->tfc &&
605 this->manual_prio == other->manual_prio &&
606 this->replay_window == other->replay_window &&
607 this->hw_offload == other->hw_offload &&
608 this->copy_dscp == other->copy_dscp &&
609 streq(this->updown, other->updown) &&
610 streq(this->interface, other->interface);
611 }
612
613 METHOD(child_cfg_t, get_ref, child_cfg_t*,
614 private_child_cfg_t *this)
615 {
616 ref_get(&this->refcount);
617 return &this->public;
618 }
619
620 METHOD(child_cfg_t, destroy, void,
621 private_child_cfg_t *this)
622 {
623 if (ref_put(&this->refcount))
624 {
625 this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
626 this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
627 this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
628 free(this->updown);
629 free(this->interface);
630 free(this->name);
631 free(this);
632 }
633 }
634
635 /*
636 * Described in header-file
637 */
638 child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
639 {
640 private_child_cfg_t *this;
641
642 INIT(this,
643 .public = {
644 .get_name = _get_name,
645 .add_traffic_selector = _add_traffic_selector,
646 .get_traffic_selectors = _get_traffic_selectors,
647 .add_proposal = _add_proposal,
648 .get_proposals = _get_proposals,
649 .select_proposal = _select_proposal,
650 .get_updown = _get_updown,
651 .get_mode = _get_mode,
652 .get_start_action = _get_start_action,
653 .get_dpd_action = _get_dpd_action,
654 .get_close_action = _get_close_action,
655 .get_lifetime = _get_lifetime,
656 .get_dh_group = _get_dh_group,
657 .get_inactivity = _get_inactivity,
658 .get_reqid = _get_reqid,
659 .get_if_id = _get_if_id,
660 .get_mark = _get_mark,
661 .get_set_mark = _get_set_mark,
662 .get_tfc = _get_tfc,
663 .get_manual_prio = _get_manual_prio,
664 .get_interface = _get_interface,
665 .get_replay_window = _get_replay_window,
666 .set_replay_window = _set_replay_window,
667 .has_option = _has_option,
668 .equals = _equals,
669 .get_ref = _get_ref,
670 .destroy = _destroy,
671 .get_hw_offload = _get_hw_offload,
672 .get_copy_dscp = _get_copy_dscp,
673 },
674 .name = strdup(name),
675 .options = data->options,
676 .updown = strdupnull(data->updown),
677 .reqid = data->reqid,
678 .if_id_in = data->if_id_in,
679 .if_id_out = data->if_id_out,
680 .mode = data->mode,
681 .start_action = data->start_action,
682 .dpd_action = data->dpd_action,
683 .close_action = data->close_action,
684 .mark_in = data->mark_in,
685 .mark_out = data->mark_out,
686 .set_mark_in = data->set_mark_in,
687 .set_mark_out = data->set_mark_out,
688 .lifetime = data->lifetime,
689 .inactivity = data->inactivity,
690 .tfc = data->tfc,
691 .manual_prio = data->priority,
692 .interface = strdupnull(data->interface),
693 .refcount = 1,
694 .proposals = linked_list_create(),
695 .my_ts = linked_list_create(),
696 .other_ts = linked_list_create(),
697 .replay_window = lib->settings->get_int(lib->settings,
698 "%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
699 .hw_offload = data->hw_offload,
700 .copy_dscp = data->copy_dscp,
701 );
702
703 return &this->public;
704 }