Moving charon to libcharon.
[strongswan.git] / src / libcharon / config / child_cfg.c
1 /*
2 * Copyright (C) 2008-2009 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 <daemon.h>
21
22 ENUM(action_names, ACTION_NONE, ACTION_RESTART,
23 "clear",
24 "hold",
25 "restart",
26 );
27
28 ENUM_BEGIN(ipcomp_transform_names, IPCOMP_NONE, IPCOMP_NONE,
29 "IPCOMP_NONE");
30 ENUM_NEXT(ipcomp_transform_names, IPCOMP_OUI, IPCOMP_LZJH, IPCOMP_NONE,
31 "IPCOMP_OUI",
32 "IPCOMP_DEFLATE",
33 "IPCOMP_LZS",
34 "IPCOMP_LZJH");
35 ENUM_END(ipcomp_transform_names, IPCOMP_LZJH);
36
37 typedef struct private_child_cfg_t private_child_cfg_t;
38
39 /**
40 * Private data of an child_cfg_t object
41 */
42 struct private_child_cfg_t {
43
44 /**
45 * Public part
46 */
47 child_cfg_t public;
48
49 /**
50 * Number of references hold by others to this child_cfg
51 */
52 refcount_t refcount;
53
54 /**
55 * Name of the child_cfg, used to query it
56 */
57 char *name;
58
59 /**
60 * list for all proposals
61 */
62 linked_list_t *proposals;
63
64 /**
65 * list for traffic selectors for my site
66 */
67 linked_list_t *my_ts;
68
69 /**
70 * list for traffic selectors for others site
71 */
72 linked_list_t *other_ts;
73
74 /**
75 * updown script
76 */
77 char *updown;
78
79 /**
80 * allow host access
81 */
82 bool hostaccess;
83
84 /**
85 * Mode to propose for a initiated CHILD: tunnel/transport
86 */
87 ipsec_mode_t mode;
88
89 /**
90 * action to take on DPD
91 */
92 action_t dpd_action;
93
94 /**
95 * action to take on CHILD_SA close
96 */
97 action_t close_action;
98
99 /**
100 * CHILD_SA lifetime config
101 */
102 lifetime_cfg_t lifetime;
103
104 /**
105 * enable IPComp
106 */
107 bool use_ipcomp;
108
109 /**
110 * Inactivity timeout
111 */
112 u_int32_t inactivity;
113
114 /**
115 * set up IPsec transport SA in MIPv6 proxy mode
116 */
117 bool proxy_mode;
118
119 /**
120 * enable installation and removal of kernel IPsec policies
121 */
122 bool install_policy;
123 };
124
125 /**
126 * Implementation of child_cfg_t.get_name.
127 */
128 static char *get_name(private_child_cfg_t *this)
129 {
130 return this->name;
131 }
132
133 /**
134 * Implementation of child_cfg_t.add_proposal.
135 */
136 static void add_proposal(private_child_cfg_t *this, proposal_t *proposal)
137 {
138 this->proposals->insert_last(this->proposals, proposal);
139 }
140
141 /**
142 * Implementation of child_cfg_t.get_proposals.
143 */
144 static linked_list_t* get_proposals(private_child_cfg_t *this, bool strip_dh)
145 {
146 enumerator_t *enumerator;
147 proposal_t *current;
148 linked_list_t *proposals = linked_list_create();
149
150 enumerator = this->proposals->create_enumerator(this->proposals);
151 while (enumerator->enumerate(enumerator, &current))
152 {
153 current = current->clone(current);
154 if (strip_dh)
155 {
156 current->strip_dh(current);
157 }
158 proposals->insert_last(proposals, current);
159 }
160 enumerator->destroy(enumerator);
161
162 return proposals;
163 }
164
165 /**
166 * Implementation of child_cfg_t.select_proposal.
167 */
168 static proposal_t* select_proposal(private_child_cfg_t*this,
169 linked_list_t *proposals, bool strip_dh,
170 bool private)
171 {
172 enumerator_t *stored_enum, *supplied_enum;
173 proposal_t *stored, *supplied, *selected = NULL;
174
175 stored_enum = this->proposals->create_enumerator(this->proposals);
176 supplied_enum = proposals->create_enumerator(proposals);
177
178 /* compare all stored proposals with all supplied. Stored ones are preferred. */
179 while (stored_enum->enumerate(stored_enum, &stored))
180 {
181 stored = stored->clone(stored);
182 while (supplied_enum->enumerate(supplied_enum, &supplied))
183 {
184 if (strip_dh)
185 {
186 stored->strip_dh(stored);
187 }
188 selected = stored->select(stored, supplied, private);
189 if (selected)
190 {
191 DBG2(DBG_CFG, "received proposals: %#P", proposals);
192 DBG2(DBG_CFG, "configured proposals: %#P", this->proposals);
193 DBG2(DBG_CFG, "selected proposal: %P", selected);
194 break;
195 }
196 }
197 stored->destroy(stored);
198 if (selected)
199 {
200 break;
201 }
202 supplied_enum->destroy(supplied_enum);
203 supplied_enum = proposals->create_enumerator(proposals);
204 }
205 stored_enum->destroy(stored_enum);
206 supplied_enum->destroy(supplied_enum);
207 if (selected == NULL)
208 {
209 DBG1(DBG_CFG, "received proposals: %#P", proposals);
210 DBG1(DBG_CFG, "configured proposals: %#P", this->proposals);
211 }
212 return selected;
213 }
214
215 /**
216 * Implementation of child_cfg_t.add_traffic_selector.
217 */
218 static void add_traffic_selector(private_child_cfg_t *this, bool local,
219 traffic_selector_t *ts)
220 {
221 if (local)
222 {
223 this->my_ts->insert_last(this->my_ts, ts);
224 }
225 else
226 {
227 this->other_ts->insert_last(this->other_ts, ts);
228 }
229 }
230
231 /**
232 * Implementation of child_cfg_t.get_traffic_selectors.
233 */
234 static linked_list_t* get_traffic_selectors(private_child_cfg_t *this, bool local,
235 linked_list_t *supplied,
236 host_t *host)
237 {
238 enumerator_t *e1, *e2;
239 traffic_selector_t *ts1, *ts2, *selected;
240 linked_list_t *result = linked_list_create();
241
242 if (local)
243 {
244 e1 = this->my_ts->create_enumerator(this->my_ts);
245 }
246 else
247 {
248 e1 = this->other_ts->create_enumerator(this->other_ts);
249 }
250
251 /* no list supplied, just fetch the stored traffic selectors */
252 if (supplied == NULL)
253 {
254 DBG2(DBG_CFG, "proposing traffic selectors for %s:",
255 local ? "us" : "other");
256 while (e1->enumerate(e1, &ts1))
257 {
258 /* we make a copy of the TS, this allows us to update dynamic TS' */
259 selected = ts1->clone(ts1);
260 if (host)
261 {
262 selected->set_address(selected, host);
263 }
264 DBG2(DBG_CFG, " %R (derived from %R)", selected, ts1);
265 result->insert_last(result, selected);
266 }
267 e1->destroy(e1);
268 }
269 else
270 {
271 DBG2(DBG_CFG, "selecting traffic selectors for %s:",
272 local ? "us" : "other");
273 e2 = supplied->create_enumerator(supplied);
274 /* iterate over all stored selectors */
275 while (e1->enumerate(e1, &ts1))
276 {
277 /* we make a copy of the TS, as we have to update dynamic TS' */
278 ts1 = ts1->clone(ts1);
279 if (host)
280 {
281 ts1->set_address(ts1, host);
282 }
283
284 /* iterate over all supplied traffic selectors */
285 while (e2->enumerate(e2, &ts2))
286 {
287 selected = ts1->get_subset(ts1, ts2);
288 if (selected)
289 {
290 DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
291 ts1, ts2, selected);
292 result->insert_last(result, selected);
293 }
294 else
295 {
296 DBG2(DBG_CFG, " config: %R, received: %R => no match",
297 ts1, ts2);
298 }
299 }
300 e2->destroy(e2);
301 e2 = supplied->create_enumerator(supplied);
302 ts1->destroy(ts1);
303 }
304 e1->destroy(e1);
305 e2->destroy(e2);
306 }
307
308 /* remove any redundant traffic selectors in the list */
309 e1 = result->create_enumerator(result);
310 e2 = result->create_enumerator(result);
311 while (e1->enumerate(e1, &ts1))
312 {
313 while (e2->enumerate(e2, &ts2))
314 {
315 if (ts1 != ts2)
316 {
317 if (ts2->is_contained_in(ts2, ts1))
318 {
319 result->remove_at(result, e2);
320 ts2->destroy(ts2);
321 e1->destroy(e1);
322 e1 = result->create_enumerator(result);
323 break;
324 }
325 if (ts1->is_contained_in(ts1, ts2))
326 {
327 result->remove_at(result, e1);
328 ts1->destroy(ts1);
329 e2->destroy(e2);
330 e2 = result->create_enumerator(result);
331 break;
332 }
333 }
334 }
335 }
336 e1->destroy(e1);
337 e2->destroy(e2);
338
339 return result;
340 }
341
342 /**
343 * Implementation of child_cfg_t.get_updown.
344 */
345 static char* get_updown(private_child_cfg_t *this)
346 {
347 return this->updown;
348 }
349
350 /**
351 * Implementation of child_cfg_t.get_hostaccess.
352 */
353 static bool get_hostaccess(private_child_cfg_t *this)
354 {
355 return this->hostaccess;
356 }
357
358 /**
359 * Applies jitter to the rekey value. Returns the new rekey value.
360 * Note: The distribution of random values is not perfect, but it
361 * should get the job done.
362 */
363 static u_int64_t apply_jitter(u_int64_t rekey, u_int64_t jitter)
364 {
365 if (jitter == 0)
366 {
367 return rekey;
368 }
369 jitter = (jitter == UINT64_MAX) ? jitter : jitter + 1;
370 return rekey - jitter * (random() / (RAND_MAX + 1.0));
371 }
372 #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
373
374 /**
375 * Implementation of child_cfg_t.get_lifetime.
376 */
377 static lifetime_cfg_t *get_lifetime(private_child_cfg_t *this)
378 {
379 lifetime_cfg_t *lft = malloc_thing(lifetime_cfg_t);
380 memcpy(lft, &this->lifetime, sizeof(lifetime_cfg_t));
381 APPLY_JITTER(lft->time);
382 APPLY_JITTER(lft->bytes);
383 APPLY_JITTER(lft->packets);
384 return lft;
385 }
386
387 /**
388 * Implementation of child_cfg_t.get_mode.
389 */
390 static ipsec_mode_t get_mode(private_child_cfg_t *this)
391 {
392 return this->mode;
393 }
394
395 /**
396 * Implementation of child_cfg_t.get_dpd_action.
397 */
398 static action_t get_dpd_action(private_child_cfg_t *this)
399 {
400 return this->dpd_action;
401 }
402
403 /**
404 * Implementation of child_cfg_t.get_close_action.
405 */
406 static action_t get_close_action(private_child_cfg_t *this)
407 {
408 return this->close_action;
409 }
410
411 /**
412 * Implementation of child_cfg_t.get_dh_group.
413 */
414 static diffie_hellman_group_t get_dh_group(private_child_cfg_t *this)
415 {
416 enumerator_t *enumerator;
417 proposal_t *proposal;
418 u_int16_t dh_group = MODP_NONE;
419
420 enumerator = this->proposals->create_enumerator(this->proposals);
421 while (enumerator->enumerate(enumerator, &proposal))
422 {
423 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
424 {
425 break;
426 }
427 }
428 enumerator->destroy(enumerator);
429 return dh_group;
430 }
431
432 /**
433 * Implementation of child_cfg_t.use_ipcomp.
434 */
435 static bool use_ipcomp(private_child_cfg_t *this)
436 {
437 return this->use_ipcomp;
438 }
439
440 /**
441 * Implementation of child_cfg_t.get_inactivity.
442 */
443 static u_int32_t get_inactivity(private_child_cfg_t *this)
444 {
445 return this->inactivity;
446 }
447
448 /**
449 * Implementation of child_cfg_t.set_mipv6_options.
450 */
451 static void set_mipv6_options(private_child_cfg_t *this, bool proxy_mode,
452 bool install_policy)
453 {
454 this->proxy_mode = proxy_mode;
455 this->install_policy = install_policy;
456 }
457
458 /**
459 * Implementation of child_cfg_t.use_proxy_mode.
460 */
461 static bool use_proxy_mode(private_child_cfg_t *this)
462 {
463 return this->proxy_mode;
464 }
465
466 /**
467 * Implementation of child_cfg_t.install_policy.
468 */
469 static bool install_policy(private_child_cfg_t *this)
470 {
471 return this->install_policy;
472 }
473
474 /**
475 * Implementation of child_cfg_t.get_ref.
476 */
477 static child_cfg_t* get_ref(private_child_cfg_t *this)
478 {
479 ref_get(&this->refcount);
480 return &this->public;
481 }
482
483 /**
484 * Implements child_cfg_t.destroy.
485 */
486 static void destroy(private_child_cfg_t *this)
487 {
488 if (ref_put(&this->refcount))
489 {
490 this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
491 this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
492 this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
493 if (this->updown)
494 {
495 free(this->updown);
496 }
497 free(this->name);
498 free(this);
499 }
500 }
501
502 /*
503 * Described in header-file
504 */
505 child_cfg_t *child_cfg_create(char *name, lifetime_cfg_t *lifetime,
506 char *updown, bool hostaccess,
507 ipsec_mode_t mode, action_t dpd_action,
508 action_t close_action, bool ipcomp,
509 u_int32_t inactivity)
510 {
511 private_child_cfg_t *this = malloc_thing(private_child_cfg_t);
512
513 this->public.get_name = (char* (*) (child_cfg_t*))get_name;
514 this->public.add_traffic_selector = (void (*)(child_cfg_t*,bool,traffic_selector_t*))add_traffic_selector;
515 this->public.get_traffic_selectors = (linked_list_t*(*)(child_cfg_t*,bool,linked_list_t*,host_t*))get_traffic_selectors;
516 this->public.add_proposal = (void (*) (child_cfg_t*,proposal_t*))add_proposal;
517 this->public.get_proposals = (linked_list_t* (*) (child_cfg_t*,bool))get_proposals;
518 this->public.select_proposal = (proposal_t* (*) (child_cfg_t*,linked_list_t*,bool,bool))select_proposal;
519 this->public.get_updown = (char* (*) (child_cfg_t*))get_updown;
520 this->public.get_hostaccess = (bool (*) (child_cfg_t*))get_hostaccess;
521 this->public.get_mode = (ipsec_mode_t (*) (child_cfg_t *))get_mode;
522 this->public.get_dpd_action = (action_t (*) (child_cfg_t *))get_dpd_action;
523 this->public.get_close_action = (action_t (*) (child_cfg_t *))get_close_action;
524 this->public.get_lifetime = (lifetime_cfg_t* (*) (child_cfg_t *))get_lifetime;
525 this->public.get_dh_group = (diffie_hellman_group_t(*)(child_cfg_t*)) get_dh_group;
526 this->public.set_mipv6_options = (void (*) (child_cfg_t*,bool,bool))set_mipv6_options;
527 this->public.use_ipcomp = (bool (*) (child_cfg_t *))use_ipcomp;
528 this->public.get_inactivity = (u_int32_t (*) (child_cfg_t *))get_inactivity;
529 this->public.use_proxy_mode = (bool (*) (child_cfg_t *))use_proxy_mode;
530 this->public.install_policy = (bool (*) (child_cfg_t *))install_policy;
531 this->public.get_ref = (child_cfg_t* (*) (child_cfg_t*))get_ref;
532 this->public.destroy = (void (*) (child_cfg_t*))destroy;
533
534 this->name = strdup(name);
535 this->updown = updown ? strdup(updown) : NULL;
536 this->hostaccess = hostaccess;
537 this->mode = mode;
538 this->dpd_action = dpd_action;
539 this->close_action = close_action;
540 this->use_ipcomp = ipcomp;
541 this->inactivity = inactivity;
542 this->proxy_mode = FALSE;
543 this->install_policy = TRUE;
544 this->refcount = 1;
545 this->proposals = linked_list_create();
546 this->my_ts = linked_list_create();
547 this->other_ts = linked_list_create();
548 memcpy(&this->lifetime, lifetime, sizeof(lifetime_cfg_t));
549
550 return &this->public;
551 }
552