]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libcharon/config/ike_cfg.c
uclibc only defines strndup(3) if _GNU_SOURCE is defined
[thirdparty/strongswan.git] / src / libcharon / config / ike_cfg.c
1 /*
2 * Copyright (C) 2005-2007 Martin Willi
3 * Copyright (C) 2005 Jan Hutter
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE /* for stdndup() */
18 #include <string.h>
19
20 #include "ike_cfg.h"
21
22 #include <daemon.h>
23
24 ENUM(ike_version_names, IKE_ANY, IKEV2,
25 "IKEv1/2",
26 "IKEv1",
27 "IKEv2",
28 );
29
30 typedef struct private_ike_cfg_t private_ike_cfg_t;
31
32 /**
33 * Private data of an ike_cfg_t object
34 */
35 struct private_ike_cfg_t {
36
37 /**
38 * Public part
39 */
40 ike_cfg_t public;
41
42 /**
43 * Number of references hold by others to this ike_cfg
44 */
45 refcount_t refcount;
46
47 /**
48 * IKE version to use
49 */
50 ike_version_t version;
51
52 /**
53 * Address list string for local host
54 */
55 char *me;
56
57 /**
58 * Address list string for remote host
59 */
60 char *other;
61
62 /**
63 * Local single host or DNS names, as allocated char*
64 */
65 linked_list_t *my_hosts;
66
67 /**
68 * Remote single host or DNS names, as allocated char*
69 */
70 linked_list_t *other_hosts;
71
72 /**
73 * Local ranges/subnets this config matches to, as traffic_selector_t*
74 */
75 linked_list_t *my_ranges;
76
77 /**
78 * Remote ranges/subnets this config matches to, as traffic_selector_t*
79 */
80 linked_list_t *other_ranges;
81
82 /**
83 * our source port
84 */
85 u_int16_t my_port;
86
87 /**
88 * destination port
89 */
90 u_int16_t other_port;
91
92 /**
93 * should we send a certificate request?
94 */
95 bool certreq;
96
97 /**
98 * enforce UDP encapsulation
99 */
100 bool force_encap;
101
102 /**
103 * use IKEv1 fragmentation
104 */
105 fragmentation_t fragmentation;
106
107 /**
108 * DSCP value to use on sent IKE packets
109 */
110 u_int8_t dscp;
111
112 /**
113 * List of proposals to use
114 */
115 linked_list_t *proposals;
116 };
117
118 METHOD(ike_cfg_t, get_version, ike_version_t,
119 private_ike_cfg_t *this)
120 {
121 return this->version;
122 }
123
124 METHOD(ike_cfg_t, send_certreq, bool,
125 private_ike_cfg_t *this)
126 {
127 return this->certreq;
128 }
129
130 METHOD(ike_cfg_t, force_encap_, bool,
131 private_ike_cfg_t *this)
132 {
133 return this->force_encap;
134 }
135
136 METHOD(ike_cfg_t, fragmentation, fragmentation_t,
137 private_ike_cfg_t *this)
138 {
139 return this->fragmentation;
140 }
141
142 /**
143 * Common function for resolve_me/other
144 */
145 static host_t* resolve(linked_list_t *hosts, int family, u_int16_t port)
146 {
147 enumerator_t *enumerator;
148 host_t *host = NULL;
149 bool tried = FALSE;
150 char *str;
151
152 enumerator = hosts->create_enumerator(hosts);
153 while (enumerator->enumerate(enumerator, &str))
154 {
155 host = host_create_from_dns(str, family, port);
156 if (host)
157 {
158 break;
159 }
160 tried = TRUE;
161 }
162 enumerator->destroy(enumerator);
163
164 if (!host && !tried)
165 {
166 /* we have no single host configured, return %any */
167 host = host_create_any(family ?: AF_INET);
168 host->set_port(host, port);
169 }
170 return host;
171 }
172
173 METHOD(ike_cfg_t, resolve_me, host_t*,
174 private_ike_cfg_t *this, int family)
175 {
176 return resolve(this->my_hosts, family, this->my_port);
177 }
178
179 METHOD(ike_cfg_t, resolve_other, host_t*,
180 private_ike_cfg_t *this, int family)
181 {
182 return resolve(this->other_hosts, family, this->other_port);
183 }
184
185 /**
186 * Common function for match_me/other
187 */
188 static u_int match(linked_list_t *hosts, linked_list_t *ranges, host_t *cand)
189 {
190 enumerator_t *enumerator;
191 traffic_selector_t *ts;
192 char *str;
193 host_t *host;
194 u_int8_t mask;
195 u_int quality = 0;
196
197 /* try single hosts first */
198 enumerator = hosts->create_enumerator(hosts);
199 while (enumerator->enumerate(enumerator, &str))
200 {
201 host = host_create_from_dns(str, cand->get_family(cand), 0);
202 if (host)
203 {
204 if (host->ip_equals(host, cand))
205 {
206 quality = max(quality, 128 + 1);
207 }
208 if (host->is_anyaddr(host))
209 {
210 quality = max(quality, 1);
211 }
212 host->destroy(host);
213 }
214 }
215 enumerator->destroy(enumerator);
216
217 /* then ranges/subnets */
218 enumerator = ranges->create_enumerator(ranges);
219 while (enumerator->enumerate(enumerator, &ts))
220 {
221 if (ts->includes(ts, cand))
222 {
223 if (ts->to_subnet(ts, &host, &mask))
224 {
225 quality = max(quality, mask + 1);
226 host->destroy(host);
227 }
228 else
229 {
230 quality = max(quality, 1);
231 }
232 }
233 }
234 enumerator->destroy(enumerator);
235
236 return quality;
237 }
238
239 METHOD(ike_cfg_t, match_me, u_int,
240 private_ike_cfg_t *this, host_t *host)
241 {
242 return match(this->my_hosts, this->my_ranges, host);
243 }
244
245 METHOD(ike_cfg_t, match_other, u_int,
246 private_ike_cfg_t *this, host_t *host)
247 {
248 return match(this->other_hosts, this->other_ranges, host);
249 }
250
251 METHOD(ike_cfg_t, get_my_addr, char*,
252 private_ike_cfg_t *this)
253 {
254 return this->me;
255 }
256
257 METHOD(ike_cfg_t, get_other_addr, char*,
258 private_ike_cfg_t *this)
259 {
260 return this->other;
261 }
262
263 METHOD(ike_cfg_t, get_my_port, u_int16_t,
264 private_ike_cfg_t *this)
265 {
266 return this->my_port;
267 }
268
269 METHOD(ike_cfg_t, get_other_port, u_int16_t,
270 private_ike_cfg_t *this)
271 {
272 return this->other_port;
273 }
274
275 METHOD(ike_cfg_t, get_dscp, u_int8_t,
276 private_ike_cfg_t *this)
277 {
278 return this->dscp;
279 }
280
281 METHOD(ike_cfg_t, add_proposal, void,
282 private_ike_cfg_t *this, proposal_t *proposal)
283 {
284 this->proposals->insert_last(this->proposals, proposal);
285 }
286
287 METHOD(ike_cfg_t, get_proposals, linked_list_t*,
288 private_ike_cfg_t *this)
289 {
290 enumerator_t *enumerator;
291 proposal_t *current;
292 linked_list_t *proposals;
293
294 proposals = linked_list_create();
295 enumerator = this->proposals->create_enumerator(this->proposals);
296 while (enumerator->enumerate(enumerator, &current))
297 {
298 current = current->clone(current);
299 proposals->insert_last(proposals, current);
300 }
301 enumerator->destroy(enumerator);
302
303 DBG2(DBG_CFG, "configured proposals: %#P", proposals);
304
305 return proposals;
306 }
307
308 METHOD(ike_cfg_t, select_proposal, proposal_t*,
309 private_ike_cfg_t *this, linked_list_t *proposals, bool private)
310 {
311 enumerator_t *stored_enum, *supplied_enum;
312 proposal_t *stored, *supplied, *selected;
313
314 stored_enum = this->proposals->create_enumerator(this->proposals);
315 supplied_enum = proposals->create_enumerator(proposals);
316
317
318 /* compare all stored proposals with all supplied. Stored ones are preferred.*/
319 while (stored_enum->enumerate(stored_enum, (void**)&stored))
320 {
321 proposals->reset_enumerator(proposals, supplied_enum);
322
323 while (supplied_enum->enumerate(supplied_enum, (void**)&supplied))
324 {
325 selected = stored->select(stored, supplied, private);
326 if (selected)
327 {
328 /* they match, return */
329 stored_enum->destroy(stored_enum);
330 supplied_enum->destroy(supplied_enum);
331 DBG2(DBG_CFG, "received proposals: %#P", proposals);
332 DBG2(DBG_CFG, "configured proposals: %#P", this->proposals);
333 DBG2(DBG_CFG, "selected proposal: %P", selected);
334 return selected;
335 }
336 }
337 }
338 /* no proposal match :-(, will result in a NO_PROPOSAL_CHOSEN... */
339 stored_enum->destroy(stored_enum);
340 supplied_enum->destroy(supplied_enum);
341 DBG1(DBG_CFG, "received proposals: %#P", proposals);
342 DBG1(DBG_CFG, "configured proposals: %#P", this->proposals);
343
344 return NULL;
345 }
346
347 METHOD(ike_cfg_t, get_dh_group, diffie_hellman_group_t,
348 private_ike_cfg_t *this)
349 {
350 enumerator_t *enumerator;
351 proposal_t *proposal;
352 u_int16_t dh_group = MODP_NONE;
353
354 enumerator = this->proposals->create_enumerator(this->proposals);
355 while (enumerator->enumerate(enumerator, &proposal))
356 {
357 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
358 {
359 break;
360 }
361 }
362 enumerator->destroy(enumerator);
363 return dh_group;
364 }
365
366 METHOD(ike_cfg_t, equals, bool,
367 private_ike_cfg_t *this, ike_cfg_t *other_public)
368 {
369 private_ike_cfg_t *other = (private_ike_cfg_t*)other_public;
370 enumerator_t *e1, *e2;
371 proposal_t *p1, *p2;
372 bool eq = TRUE;
373
374 if (this == other)
375 {
376 return TRUE;
377 }
378 if (this->public.equals != other->public.equals)
379 {
380 return FALSE;
381 }
382 if (this->proposals->get_count(this->proposals) !=
383 other->proposals->get_count(other->proposals))
384 {
385 return FALSE;
386 }
387 e1 = this->proposals->create_enumerator(this->proposals);
388 e2 = this->proposals->create_enumerator(this->proposals);
389 while (e1->enumerate(e1, &p1) && e2->enumerate(e2, &p2))
390 {
391 if (!p1->equals(p1, p2))
392 {
393 eq = FALSE;
394 break;
395 }
396 }
397 e1->destroy(e1);
398 e2->destroy(e2);
399
400 return (eq &&
401 this->version == other->version &&
402 this->certreq == other->certreq &&
403 this->force_encap == other->force_encap &&
404 this->fragmentation == other->fragmentation &&
405 streq(this->me, other->me) &&
406 streq(this->other, other->other) &&
407 this->my_port == other->my_port &&
408 this->other_port == other->other_port);
409 }
410
411 METHOD(ike_cfg_t, get_ref, ike_cfg_t*,
412 private_ike_cfg_t *this)
413 {
414 ref_get(&this->refcount);
415 return &this->public;
416 }
417
418 METHOD(ike_cfg_t, destroy, void,
419 private_ike_cfg_t *this)
420 {
421 if (ref_put(&this->refcount))
422 {
423 this->proposals->destroy_offset(this->proposals,
424 offsetof(proposal_t, destroy));
425 free(this->me);
426 free(this->other);
427 this->my_hosts->destroy_function(this->my_hosts, free);
428 this->other_hosts->destroy_function(this->other_hosts, free);
429 this->my_ranges->destroy_offset(this->my_ranges,
430 offsetof(traffic_selector_t, destroy));
431 this->other_ranges->destroy_offset(this->other_ranges,
432 offsetof(traffic_selector_t, destroy));
433 free(this);
434 }
435 }
436
437 /**
438 * Try to parse a string as subnet
439 */
440 static traffic_selector_t* make_subnet(char *str)
441 {
442 char *pos;
443
444 pos = strchr(str, '/');
445 if (!pos)
446 {
447 return NULL;
448 }
449 return traffic_selector_create_from_cidr(str, 0, 0, 0);
450 }
451
452 /**
453 * Try to parse a string as an IP range
454 */
455 static traffic_selector_t* make_range(char *str)
456 {
457 traffic_selector_t *ts;
458 ts_type_t type;
459 char *pos;
460 host_t *from, *to;
461
462 pos = strchr(str, '-');
463 if (!pos)
464 {
465 return NULL;
466 }
467 to = host_create_from_string(pos + 1, 0);
468 if (!to)
469 {
470 return NULL;
471 }
472 str = strndup(str, pos - str);
473 from = host_create_from_string_and_family(str, to->get_family(to), 0);
474 free(str);
475 if (!from)
476 {
477 to->destroy(to);
478 return NULL;
479 }
480 if (to->get_family(to) == AF_INET)
481 {
482 type = TS_IPV4_ADDR_RANGE;
483 }
484 else
485 {
486 type = TS_IPV6_ADDR_RANGE;
487 }
488 ts = traffic_selector_create_from_bytes(0, type,
489 from->get_address(from), 0,
490 to->get_address(to), 0);
491 from->destroy(from);
492 to->destroy(to);
493 return ts;
494 }
495
496 /**
497 * Parse address string into lists of single hosts and ranges/subnets
498 */
499 static void parse_addresses(char *str, linked_list_t *hosts,
500 linked_list_t *ranges)
501 {
502 enumerator_t *enumerator;
503 traffic_selector_t *ts;
504
505 enumerator = enumerator_create_token(str, ",", " ");
506 while (enumerator->enumerate(enumerator, &str))
507 {
508 ts = make_subnet(str);
509 if (ts)
510 {
511 ranges->insert_last(ranges, ts);
512 continue;
513 }
514 ts = make_range(str);
515 if (ts)
516 {
517 ranges->insert_last(ranges, ts);
518 continue;
519 }
520 hosts->insert_last(hosts, strdup(str));
521 }
522 enumerator->destroy(enumerator);
523 }
524
525 /**
526 * Described in header.
527 */
528 ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
529 char *me, u_int16_t my_port,
530 char *other, u_int16_t other_port,
531 fragmentation_t fragmentation, u_int8_t dscp)
532 {
533 private_ike_cfg_t *this;
534
535 INIT(this,
536 .public = {
537 .get_version = _get_version,
538 .send_certreq = _send_certreq,
539 .force_encap = _force_encap_,
540 .fragmentation = _fragmentation,
541 .resolve_me = _resolve_me,
542 .resolve_other = _resolve_other,
543 .match_me = _match_me,
544 .match_other = _match_other,
545 .get_my_addr = _get_my_addr,
546 .get_other_addr = _get_other_addr,
547 .get_my_port = _get_my_port,
548 .get_other_port = _get_other_port,
549 .get_dscp = _get_dscp,
550 .add_proposal = _add_proposal,
551 .get_proposals = _get_proposals,
552 .select_proposal = _select_proposal,
553 .get_dh_group = _get_dh_group,
554 .equals = _equals,
555 .get_ref = _get_ref,
556 .destroy = _destroy,
557 },
558 .refcount = 1,
559 .version = version,
560 .certreq = certreq,
561 .force_encap = force_encap,
562 .fragmentation = fragmentation,
563 .me = strdup(me),
564 .my_ranges = linked_list_create(),
565 .my_hosts = linked_list_create(),
566 .other = strdup(other),
567 .other_ranges = linked_list_create(),
568 .other_hosts = linked_list_create(),
569 .my_port = my_port,
570 .other_port = other_port,
571 .dscp = dscp,
572 .proposals = linked_list_create(),
573 );
574
575 parse_addresses(me, this->my_hosts, this->my_ranges);
576 parse_addresses(other, this->other_hosts, this->other_ranges);
577
578 return &this->public;
579 }