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