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