Removed strayed code fragment
[strongswan.git] / src / charon / plugins / stroke / stroke_config.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "stroke_config.h"
17
18 #include <daemon.h>
19 #include <threading/mutex.h>
20 #include <utils/lexparser.h>
21
22 typedef struct private_stroke_config_t private_stroke_config_t;
23
24 /**
25 * private data of stroke_config
26 */
27 struct private_stroke_config_t {
28
29 /**
30 * public functions
31 */
32 stroke_config_t public;
33
34 /**
35 * list of peer_cfg_t
36 */
37 linked_list_t *list;
38
39 /**
40 * mutex to lock config list
41 */
42 mutex_t *mutex;
43
44 /**
45 * ca sections
46 */
47 stroke_ca_t *ca;
48
49 /**
50 * credentials
51 */
52 stroke_cred_t *cred;
53 };
54
55 /**
56 * Implementation of backend_t.create_peer_cfg_enumerator.
57 */
58 static enumerator_t* create_peer_cfg_enumerator(private_stroke_config_t *this,
59 identification_t *me,
60 identification_t *other)
61 {
62 this->mutex->lock(this->mutex);
63 return enumerator_create_cleaner(this->list->create_enumerator(this->list),
64 (void*)this->mutex->unlock, this->mutex);
65 }
66
67 /**
68 * filter function for ike configs
69 */
70 static bool ike_filter(void *data, peer_cfg_t **in, ike_cfg_t **out)
71 {
72 *out = (*in)->get_ike_cfg(*in);
73 return TRUE;
74 }
75
76 /**
77 * Implementation of backend_t.create_ike_cfg_enumerator.
78 */
79 static enumerator_t* create_ike_cfg_enumerator(private_stroke_config_t *this,
80 host_t *me, host_t *other)
81 {
82 this->mutex->lock(this->mutex);
83 return enumerator_create_filter(this->list->create_enumerator(this->list),
84 (void*)ike_filter, this->mutex,
85 (void*)this->mutex->unlock);
86 }
87
88 /**
89 * implements backend_t.get_peer_cfg_by_name.
90 */
91 static peer_cfg_t *get_peer_cfg_by_name(private_stroke_config_t *this, char *name)
92 {
93 enumerator_t *e1, *e2;
94 peer_cfg_t *current, *found = NULL;
95 child_cfg_t *child;
96
97 this->mutex->lock(this->mutex);
98 e1 = this->list->create_enumerator(this->list);
99 while (e1->enumerate(e1, &current))
100 {
101 /* compare peer_cfgs name first */
102 if (streq(current->get_name(current), name))
103 {
104 found = current;
105 found->get_ref(found);
106 break;
107 }
108 /* compare all child_cfg names otherwise */
109 e2 = current->create_child_cfg_enumerator(current);
110 while (e2->enumerate(e2, &child))
111 {
112 if (streq(child->get_name(child), name))
113 {
114 found = current;
115 found->get_ref(found);
116 break;
117 }
118 }
119 e2->destroy(e2);
120 if (found)
121 {
122 break;
123 }
124 }
125 e1->destroy(e1);
126 this->mutex->unlock(this->mutex);
127 return found;
128 }
129
130 /**
131 * parse a proposal string, either into ike_cfg or child_cfg
132 */
133 static void add_proposals(private_stroke_config_t *this, char *string,
134 ike_cfg_t *ike_cfg, child_cfg_t *child_cfg)
135 {
136 if (string)
137 {
138 char *single;
139 char *strict;
140 proposal_t *proposal;
141 protocol_id_t proto = PROTO_ESP;
142
143 if (ike_cfg)
144 {
145 proto = PROTO_IKE;
146 }
147 strict = string + strlen(string) - 1;
148 if (*strict == '!')
149 {
150 *strict = '\0';
151 }
152 else
153 {
154 strict = NULL;
155 }
156 while ((single = strsep(&string, ",")))
157 {
158 proposal = proposal_create_from_string(proto, single);
159 if (proposal)
160 {
161 if (ike_cfg)
162 {
163 ike_cfg->add_proposal(ike_cfg, proposal);
164 }
165 else
166 {
167 child_cfg->add_proposal(child_cfg, proposal);
168 }
169 continue;
170 }
171 DBG1(DBG_CFG, "skipped invalid proposal string: %s", single);
172 }
173 if (strict)
174 {
175 return;
176 }
177 /* add default porposal to the end if not strict */
178 }
179 if (ike_cfg)
180 {
181 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
182 }
183 else
184 {
185 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
186 }
187 }
188
189 /**
190 * Build an IKE config from a stroke message
191 */
192 static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg)
193 {
194 stroke_end_t tmp_end;
195 ike_cfg_t *ike_cfg;
196 char *interface;
197 host_t *host;
198
199 host = host_create_from_dns(msg->add_conn.other.address, 0, 0);
200 if (host)
201 {
202 interface = charon->kernel_interface->get_interface(
203 charon->kernel_interface, host);
204 host->destroy(host);
205 if (interface)
206 {
207 DBG2(DBG_CFG, "left is other host, swapping ends");
208 tmp_end = msg->add_conn.me;
209 msg->add_conn.me = msg->add_conn.other;
210 msg->add_conn.other = tmp_end;
211 free(interface);
212 }
213 else
214 {
215 host = host_create_from_dns(msg->add_conn.me.address, 0, 0);
216 if (host)
217 {
218 interface = charon->kernel_interface->get_interface(
219 charon->kernel_interface, host);
220 host->destroy(host);
221 if (!interface)
222 {
223 DBG1(DBG_CFG, "left nor right host is our side, "
224 "assuming left=local");
225 }
226 else
227 {
228 free(interface);
229 }
230
231 }
232 }
233 }
234 ike_cfg = ike_cfg_create(msg->add_conn.other.sendcert != CERT_NEVER_SEND,
235 msg->add_conn.force_encap,
236 msg->add_conn.me.address, msg->add_conn.me.ikeport,
237 msg->add_conn.other.address, msg->add_conn.other.ikeport);
238 add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL);
239 return ike_cfg;
240 }
241
242 /**
243 * Add CRL constraint to config
244 */
245 static void build_crl_policy(auth_cfg_t *cfg, bool local, int policy)
246 {
247 /* CRL/OCSP policy, for remote config only */
248 if (!local)
249 {
250 switch (policy)
251 {
252 case CRL_STRICT_YES:
253 /* if yes, we require a GOOD validation */
254 cfg->add(cfg, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
255 break;
256 case CRL_STRICT_IFURI:
257 /* for ifuri, a SKIPPED validation is sufficient */
258 cfg->add(cfg, AUTH_RULE_CRL_VALIDATION, VALIDATION_SKIPPED);
259 break;
260 default:
261 break;
262 }
263 }
264 }
265
266 /**
267 * build authentication config
268 */
269 static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
270 stroke_msg_t *msg, bool local, bool primary)
271 {
272 identification_t *identity;
273 certificate_t *certificate;
274 char *auth, *id, *cert, *ca;
275 stroke_end_t *end, *other_end;
276 auth_cfg_t *cfg;
277 char eap_buf[32];
278
279 /* select strings */
280 if (local)
281 {
282 end = &msg->add_conn.me;
283 other_end = &msg->add_conn.other;
284 }
285 else
286 {
287 end = &msg->add_conn.other;
288 other_end = &msg->add_conn.me;
289 }
290 if (primary)
291 {
292 auth = end->auth;
293 id = end->id;
294 if (!id)
295 { /* leftid/rightid fallback to address */
296 id = end->address;
297 }
298 cert = end->cert;
299 ca = end->ca;
300 if (ca && streq(ca, "%same"))
301 {
302 ca = other_end->ca;
303 }
304 }
305 else
306 {
307 auth = end->auth2;
308 id = end->id2;
309 if (local && !id)
310 { /* leftid2 falls back to leftid */
311 id = end->id;
312 }
313 cert = end->cert2;
314 ca = end->ca2;
315 if (ca && streq(ca, "%same"))
316 {
317 ca = other_end->ca2;
318 }
319 }
320
321 if (!auth)
322 {
323 if (primary)
324 {
325 if (local)
326 { /* "leftauth" not defined, fall back to deprecated "authby" */
327 switch (msg->add_conn.auth_method)
328 {
329 default:
330 case AUTH_CLASS_PUBKEY:
331 auth = "pubkey";
332 break;
333 case AUTH_CLASS_PSK:
334 auth = "psk";
335 break;
336 case AUTH_CLASS_EAP:
337 auth = "eap";
338 break;
339 }
340 }
341 else
342 { /* "rightauth" not defined, fall back to deprecated "eap" */
343 if (msg->add_conn.eap_type)
344 {
345 if (msg->add_conn.eap_vendor)
346 {
347 snprintf(eap_buf, sizeof(eap_buf), "eap-%d-%d",
348 msg->add_conn.eap_type,
349 msg->add_conn.eap_vendor);
350 }
351 else
352 {
353 snprintf(eap_buf, sizeof(eap_buf), "eap-%d",
354 msg->add_conn.eap_type);
355 }
356 auth = eap_buf;
357 }
358 else
359 { /* not EAP => no constraints for this peer */
360 auth = "any";
361 }
362 }
363 }
364 else
365 { /* no second authentication round, fine */
366 return NULL;
367 }
368 }
369
370 cfg = auth_cfg_create();
371
372 /* add identity and peer certifcate */
373 identity = identification_create_from_string(id);
374 if (cert)
375 {
376 certificate = this->cred->load_peer(this->cred, cert);
377 if (certificate)
378 {
379 if (local)
380 {
381 this->ca->check_for_hash_and_url(this->ca, certificate);
382 }
383 cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate);
384 if (identity->get_type(identity) == ID_ANY ||
385 !certificate->has_subject(certificate, identity))
386 {
387 DBG1(DBG_CFG, " id '%Y' not confirmed by certificate, "
388 "defaulting to '%Y'", identity,
389 certificate->get_subject(certificate));
390 identity->destroy(identity);
391 identity = certificate->get_subject(certificate);
392 identity = identity->clone(identity);
393 }
394 }
395 }
396 cfg->add(cfg, AUTH_RULE_IDENTITY, identity);
397
398 /* CA constraint */
399 if (ca)
400 {
401 identity = identification_create_from_string(ca);
402 certificate = charon->credentials->get_cert(charon->credentials,
403 CERT_X509, KEY_ANY, identity, TRUE);
404 identity->destroy(identity);
405 if (certificate)
406 {
407 cfg->add(cfg, AUTH_RULE_CA_CERT, certificate);
408 }
409 else
410 {
411 DBG1(DBG_CFG, "CA certificate %s not found, discarding CA "
412 "constraint", ca);
413 }
414 }
415
416 /* AC groups */
417 if (end->groups)
418 {
419 enumerator_t *enumerator;
420 char *group;
421
422 enumerator = enumerator_create_token(end->groups, ",", " ");
423 while (enumerator->enumerate(enumerator, &group))
424 {
425 identity = identification_create_from_encoding(ID_IETF_ATTR_STRING,
426 chunk_create(group, strlen(group)));
427 cfg->add(cfg, AUTH_RULE_AC_GROUP, identity);
428 }
429 enumerator->destroy(enumerator);
430 }
431
432 /* authentication metod (class, actually) */
433 if (streq(auth, "pubkey") ||
434 streq(auth, "rsasig") || streq(auth, "rsa") ||
435 streq(auth, "ecdsasig") || streq(auth, "ecdsa"))
436 {
437 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
438 build_crl_policy(cfg, local, msg->add_conn.crl_policy);
439 }
440 else if (streq(auth, "psk") || streq(auth, "secret"))
441 {
442 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
443 }
444 else if (strneq(auth, "eap", 3))
445 {
446 enumerator_t *enumerator;
447 char *str;
448 int i = 0, type = 0, vendor;
449
450 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
451
452 /* parse EAP string, format: eap[-type[-vendor]] */
453 enumerator = enumerator_create_token(auth, "-", " ");
454 while (enumerator->enumerate(enumerator, &str))
455 {
456 switch (i)
457 {
458 case 1:
459 type = eap_type_from_string(str);
460 if (!type)
461 {
462 type = atoi(str);
463 if (!type)
464 {
465 DBG1(DBG_CFG, "unknown EAP method: %s", str);
466 break;
467 }
468 }
469 cfg->add(cfg, AUTH_RULE_EAP_TYPE, type);
470 break;
471 case 2:
472 if (type)
473 {
474 vendor = atoi(str);
475 if (vendor)
476 {
477 cfg->add(cfg, AUTH_RULE_EAP_VENDOR, vendor);
478 }
479 else
480 {
481 DBG1(DBG_CFG, "unknown EAP vendor: %s", str);
482 }
483 }
484 break;
485 default:
486 break;
487 }
488 i++;
489 }
490 enumerator->destroy(enumerator);
491
492 if (msg->add_conn.eap_identity)
493 {
494 if (streq(msg->add_conn.eap_identity, "%identity"))
495 {
496 identity = identification_create_from_encoding(ID_ANY,
497 chunk_empty);
498 }
499 else
500 {
501 identity = identification_create_from_string(
502 msg->add_conn.eap_identity);
503 }
504 cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, identity);
505 }
506 }
507 else
508 {
509 if (!streq(auth, "any"))
510 {
511 DBG1(DBG_CFG, "authentication method %s unknown, fallback to any",
512 auth);
513 }
514 build_crl_policy(cfg, local, msg->add_conn.crl_policy);
515 }
516 return cfg;
517 }
518
519 /**
520 * build a peer_cfg from a stroke msg
521 */
522 static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
523 stroke_msg_t *msg, ike_cfg_t *ike_cfg)
524 {
525 identification_t *peer_id = NULL;
526 peer_cfg_t *mediated_by = NULL;
527 host_t *vip = NULL;
528 unique_policy_t unique;
529 u_int32_t rekey = 0, reauth = 0, over, jitter;
530 peer_cfg_t *peer_cfg;
531 auth_cfg_t *auth_cfg;
532
533 #ifdef ME
534 if (msg->add_conn.ikeme.mediation && msg->add_conn.ikeme.mediated_by)
535 {
536 DBG1(DBG_CFG, "a mediation connection cannot be a mediated connection "
537 "at the same time, aborting");
538 return NULL;
539 }
540
541 if (msg->add_conn.ikeme.mediation)
542 {
543 /* force unique connections for mediation connections */
544 msg->add_conn.unique = 1;
545 }
546
547 if (msg->add_conn.ikeme.mediated_by)
548 {
549 mediated_by = charon->backends->get_peer_cfg_by_name(charon->backends,
550 msg->add_conn.ikeme.mediated_by);
551 if (!mediated_by)
552 {
553 DBG1(DBG_CFG, "mediation connection '%s' not found, aborting",
554 msg->add_conn.ikeme.mediated_by);
555 return NULL;
556 }
557 if (!mediated_by->is_mediation(mediated_by))
558 {
559 DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is "
560 "no mediation connection, aborting",
561 msg->add_conn.ikeme.mediated_by, msg->add_conn.name);
562 mediated_by->destroy(mediated_by);
563 return NULL;
564 }
565 if (msg->add_conn.ikeme.peerid)
566 {
567 peer_id = identification_create_from_string(msg->add_conn.ikeme.peerid);
568 }
569 else if (msg->add_conn.other.id)
570 {
571 peer_id = identification_create_from_string(msg->add_conn.other.id);
572 }
573 }
574 #endif /* ME */
575
576 jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100;
577 over = msg->add_conn.rekey.margin;
578 if (msg->add_conn.rekey.reauth)
579 {
580 reauth = msg->add_conn.rekey.ike_lifetime - over;
581 }
582 else
583 {
584 rekey = msg->add_conn.rekey.ike_lifetime - over;
585 }
586 if (msg->add_conn.me.sourceip_mask)
587 {
588 if (msg->add_conn.me.sourceip)
589 {
590 vip = host_create_from_string(msg->add_conn.me.sourceip, 0);
591 }
592 if (!vip)
593 { /* if it is set to something like %poolname, request an address */
594 if (msg->add_conn.me.subnets)
595 { /* use the same address as in subnet, if any */
596 if (strchr(msg->add_conn.me.subnets, '.'))
597 {
598 vip = host_create_any(AF_INET);
599 }
600 else
601 {
602 vip = host_create_any(AF_INET6);
603 }
604 }
605 else
606 {
607 if (strchr(ike_cfg->get_my_addr(ike_cfg), ':'))
608 {
609 vip = host_create_any(AF_INET6);
610 }
611 else
612 {
613 vip = host_create_any(AF_INET);
614 }
615 }
616 }
617 }
618 switch (msg->add_conn.unique)
619 {
620 case 1: /* yes */
621 case 2: /* replace */
622 unique = UNIQUE_REPLACE;
623 break;
624 case 3: /* keep */
625 unique = UNIQUE_KEEP;
626 break;
627 default: /* no */
628 unique = UNIQUE_NO;
629 break;
630 }
631 if (msg->add_conn.dpd.action == 0)
632 { /* dpdaction=none disables DPD */
633 msg->add_conn.dpd.delay = 0;
634 }
635
636 /* other.sourceip is managed in stroke_attributes. If it is set, we define
637 * the pool name as the connection name, which the attribute provider
638 * uses to serve pool addresses. */
639 peer_cfg = peer_cfg_create(msg->add_conn.name,
640 msg->add_conn.ikev2 ? 2 : 1, ike_cfg,
641 msg->add_conn.me.sendcert, unique,
642 msg->add_conn.rekey.tries, rekey, reauth, jitter, over,
643 msg->add_conn.mobike, msg->add_conn.dpd.delay,
644 vip, msg->add_conn.other.sourceip_mask ?
645 msg->add_conn.name : msg->add_conn.other.sourceip,
646 msg->add_conn.ikeme.mediation, mediated_by, peer_id);
647
648 /* build leftauth= */
649 auth_cfg = build_auth_cfg(this, msg, TRUE, TRUE);
650 if (auth_cfg)
651 {
652 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
653 }
654 else
655 { /* we require at least one config on our side */
656 peer_cfg->destroy(peer_cfg);
657 return NULL;
658 }
659 /* build leftauth2= */
660 auth_cfg = build_auth_cfg(this, msg, TRUE, FALSE);
661 if (auth_cfg)
662 {
663 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
664 }
665 /* build rightauth= */
666 auth_cfg = build_auth_cfg(this, msg, FALSE, TRUE);
667 if (auth_cfg)
668 {
669 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
670 }
671 /* build rightauth2= */
672 auth_cfg = build_auth_cfg(this, msg, FALSE, FALSE);
673 if (auth_cfg)
674 {
675 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
676 }
677 return peer_cfg;
678 }
679
680 /**
681 * build a traffic selector from a stroke_end
682 */
683 static void add_ts(private_stroke_config_t *this,
684 stroke_end_t *end, child_cfg_t *child_cfg, bool local)
685 {
686 traffic_selector_t *ts;
687
688 if (end->tohost)
689 {
690 ts = traffic_selector_create_dynamic(end->protocol,
691 end->port ? end->port : 0, end->port ? end->port : 65535);
692 child_cfg->add_traffic_selector(child_cfg, local, ts);
693 }
694 else
695 {
696 host_t *net;
697
698 if (!end->subnets)
699 {
700 net = host_create_from_string(end->address, 0);
701 if (net)
702 {
703 ts = traffic_selector_create_from_subnet(net, 0, end->protocol,
704 end->port);
705 child_cfg->add_traffic_selector(child_cfg, local, ts);
706 }
707 }
708 else
709 {
710 char *del, *start, *bits;
711
712 start = end->subnets;
713 do
714 {
715 int intbits = 0;
716
717 del = strchr(start, ',');
718 if (del)
719 {
720 *del = '\0';
721 }
722 bits = strchr(start, '/');
723 if (bits)
724 {
725 *bits = '\0';
726 intbits = atoi(bits + 1);
727 }
728
729 net = host_create_from_string(start, 0);
730 if (net)
731 {
732 ts = traffic_selector_create_from_subnet(net, intbits,
733 end->protocol, end->port);
734 child_cfg->add_traffic_selector(child_cfg, local, ts);
735 }
736 else
737 {
738 DBG1(DBG_CFG, "invalid subnet: %s, skipped", start);
739 }
740 start = del + 1;
741 }
742 while (del);
743 }
744 }
745 }
746
747 /**
748 * build a child config from the stroke message
749 */
750 static child_cfg_t *build_child_cfg(private_stroke_config_t *this,
751 stroke_msg_t *msg)
752 {
753 child_cfg_t *child_cfg;
754 action_t dpd;
755 lifetime_cfg_t lifetime = {
756 .time = {
757 .life = msg->add_conn.rekey.ipsec_lifetime,
758 .rekey = msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
759 .jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100
760 },
761 .bytes = {
762 .life = msg->add_conn.rekey.life_bytes,
763 .rekey = msg->add_conn.rekey.life_bytes - msg->add_conn.rekey.margin_bytes,
764 .jitter = msg->add_conn.rekey.margin_bytes * msg->add_conn.rekey.fuzz / 100
765 },
766 .packets = {
767 .life = msg->add_conn.rekey.life_packets,
768 .rekey = msg->add_conn.rekey.life_packets - msg->add_conn.rekey.margin_packets,
769 .jitter = msg->add_conn.rekey.margin_packets * msg->add_conn.rekey.fuzz / 100
770 }
771 };
772
773 switch (msg->add_conn.dpd.action)
774 { /* map startes magic values to our action type */
775 case 2: /* =hold */
776 dpd = ACTION_ROUTE;
777 break;
778 case 3: /* =restart */
779 dpd = ACTION_RESTART;
780 break;
781 default:
782 dpd = ACTION_NONE;
783 break;
784 }
785
786 child_cfg = child_cfg_create(
787 msg->add_conn.name, &lifetime,
788 msg->add_conn.me.updown, msg->add_conn.me.hostaccess,
789 msg->add_conn.mode, dpd, dpd, msg->add_conn.ipcomp,
790 msg->add_conn.inactivity);
791 child_cfg->set_mipv6_options(child_cfg, msg->add_conn.proxy_mode,
792 msg->add_conn.install_policy);
793 add_ts(this, &msg->add_conn.me, child_cfg, TRUE);
794 add_ts(this, &msg->add_conn.other, child_cfg, FALSE);
795
796 add_proposals(this, msg->add_conn.algorithms.esp, NULL, child_cfg);
797
798 return child_cfg;
799 }
800
801 /**
802 * Implementation of stroke_config_t.add.
803 */
804 static void add(private_stroke_config_t *this, stroke_msg_t *msg)
805 {
806 ike_cfg_t *ike_cfg, *existing_ike;
807 peer_cfg_t *peer_cfg, *existing;
808 child_cfg_t *child_cfg;
809 enumerator_t *enumerator;
810 bool use_existing = FALSE;
811
812 ike_cfg = build_ike_cfg(this, msg);
813 if (!ike_cfg)
814 {
815 return;
816 }
817 peer_cfg = build_peer_cfg(this, msg, ike_cfg);
818 if (!peer_cfg)
819 {
820 ike_cfg->destroy(ike_cfg);
821 return;
822 }
823
824 enumerator = create_peer_cfg_enumerator(this, NULL, NULL);
825 while (enumerator->enumerate(enumerator, &existing))
826 {
827 existing_ike = existing->get_ike_cfg(existing);
828 if (existing->equals(existing, peer_cfg) &&
829 existing_ike->equals(existing_ike, peer_cfg->get_ike_cfg(peer_cfg)))
830 {
831 use_existing = TRUE;
832 peer_cfg->destroy(peer_cfg);
833 peer_cfg = existing;
834 peer_cfg->get_ref(peer_cfg);
835 DBG1(DBG_CFG, "added child to existing configuration '%s'",
836 peer_cfg->get_name(peer_cfg));
837 break;
838 }
839 }
840 enumerator->destroy(enumerator);
841
842 child_cfg = build_child_cfg(this, msg);
843 if (!child_cfg)
844 {
845 peer_cfg->destroy(peer_cfg);
846 return;
847 }
848 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
849
850 if (use_existing)
851 {
852 peer_cfg->destroy(peer_cfg);
853 }
854 else
855 {
856 /* add config to backend */
857 DBG1(DBG_CFG, "added configuration '%s'", msg->add_conn.name);
858 this->mutex->lock(this->mutex);
859 this->list->insert_last(this->list, peer_cfg);
860 this->mutex->unlock(this->mutex);
861 }
862 }
863
864 /**
865 * Implementation of stroke_config_t.del.
866 */
867 static void del(private_stroke_config_t *this, stroke_msg_t *msg)
868 {
869 enumerator_t *enumerator, *children;
870 peer_cfg_t *peer;
871 child_cfg_t *child;
872 bool deleted = FALSE;
873
874 this->mutex->lock(this->mutex);
875 enumerator = this->list->create_enumerator(this->list);
876 while (enumerator->enumerate(enumerator, (void**)&peer))
877 {
878 bool keep = FALSE;
879
880 /* remove any child with such a name */
881 children = peer->create_child_cfg_enumerator(peer);
882 while (children->enumerate(children, &child))
883 {
884 if (streq(child->get_name(child), msg->del_conn.name))
885 {
886 peer->remove_child_cfg(peer, children);
887 child->destroy(child);
888 deleted = TRUE;
889 }
890 else
891 {
892 keep = TRUE;
893 }
894 }
895 children->destroy(children);
896
897 /* if peer config matches, or has no children anymore, remove it */
898 if (!keep || streq(peer->get_name(peer), msg->del_conn.name))
899 {
900 this->list->remove_at(this->list, enumerator);
901 peer->destroy(peer);
902 deleted = TRUE;
903 }
904 }
905 enumerator->destroy(enumerator);
906 this->mutex->unlock(this->mutex);
907
908 if (deleted)
909 {
910 DBG1(DBG_CFG, "deleted connection '%s'", msg->del_conn.name);
911 }
912 else
913 {
914 DBG1(DBG_CFG, "connection '%s' not found", msg->del_conn.name);
915 }
916 }
917
918 /**
919 * Implementation of stroke_config_t.destroy
920 */
921 static void destroy(private_stroke_config_t *this)
922 {
923 this->list->destroy_offset(this->list, offsetof(peer_cfg_t, destroy));
924 this->mutex->destroy(this->mutex);
925 free(this);
926 }
927
928 /*
929 * see header file
930 */
931 stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred)
932 {
933 private_stroke_config_t *this = malloc_thing(private_stroke_config_t);
934
935 this->public.backend.create_peer_cfg_enumerator = (enumerator_t*(*)(backend_t*, identification_t *me, identification_t *other))create_peer_cfg_enumerator;
936 this->public.backend.create_ike_cfg_enumerator = (enumerator_t*(*)(backend_t*, host_t *me, host_t *other))create_ike_cfg_enumerator;
937 this->public.backend.get_peer_cfg_by_name = (peer_cfg_t* (*)(backend_t*,char*))get_peer_cfg_by_name;
938 this->public.add = (void(*)(stroke_config_t*, stroke_msg_t *msg))add;
939 this->public.del = (void(*)(stroke_config_t*, stroke_msg_t *msg))del;
940 this->public.destroy = (void(*)(stroke_config_t*))destroy;
941
942 this->list = linked_list_create();
943 this->mutex = mutex_create(MUTEX_TYPE_RECURSIVE);
944 this->ca = ca;
945 this->cred = cred;
946
947 return &this->public;
948 }
949