Added a "aggressive" ipsec.conf connection option
[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, "xauth", 5))
473 {
474 char *pos;
475
476 pos = strchr(auth, '-');
477 if (pos)
478 {
479 cfg->add(cfg, AUTH_RULE_XAUTH_BACKEND, strdup(++pos));
480 }
481 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_XAUTH);
482 if (msg->add_conn.xauth_identity)
483 {
484 cfg->add(cfg, AUTH_RULE_XAUTH_IDENTITY,
485 identification_create_from_string(msg->add_conn.xauth_identity));
486 }
487 }
488 else if (strneq(auth, "eap", 3))
489 {
490 enumerator_t *enumerator;
491 char *str;
492 int i = 0, type = 0, vendor;
493
494 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
495
496 /* parse EAP string, format: eap[-type[-vendor]] */
497 enumerator = enumerator_create_token(auth, "-", " ");
498 while (enumerator->enumerate(enumerator, &str))
499 {
500 switch (i)
501 {
502 case 1:
503 type = eap_type_from_string(str);
504 if (!type)
505 {
506 type = atoi(str);
507 if (!type)
508 {
509 DBG1(DBG_CFG, "unknown EAP method: %s", str);
510 break;
511 }
512 }
513 cfg->add(cfg, AUTH_RULE_EAP_TYPE, type);
514 break;
515 case 2:
516 if (type)
517 {
518 vendor = atoi(str);
519 if (vendor)
520 {
521 cfg->add(cfg, AUTH_RULE_EAP_VENDOR, vendor);
522 }
523 else
524 {
525 DBG1(DBG_CFG, "unknown EAP vendor: %s", str);
526 }
527 }
528 break;
529 default:
530 break;
531 }
532 i++;
533 }
534 enumerator->destroy(enumerator);
535
536 if (msg->add_conn.eap_identity)
537 {
538 if (streq(msg->add_conn.eap_identity, "%identity"))
539 {
540 identity = identification_create_from_encoding(ID_ANY,
541 chunk_empty);
542 }
543 else
544 {
545 identity = identification_create_from_string(
546 msg->add_conn.eap_identity);
547 }
548 cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, identity);
549 }
550 if (msg->add_conn.aaa_identity)
551 {
552 cfg->add(cfg, AUTH_RULE_AAA_IDENTITY,
553 identification_create_from_string(msg->add_conn.aaa_identity));
554 }
555 }
556 else
557 {
558 if (!streq(auth, "any"))
559 {
560 DBG1(DBG_CFG, "authentication method %s unknown, fallback to any",
561 auth);
562 }
563 build_crl_policy(cfg, local, msg->add_conn.crl_policy);
564 }
565 return cfg;
566 }
567
568 /**
569 * build a peer_cfg from a stroke msg
570 */
571 static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
572 stroke_msg_t *msg, ike_cfg_t *ike_cfg)
573 {
574 identification_t *peer_id = NULL;
575 peer_cfg_t *mediated_by = NULL;
576 host_t *vip = NULL;
577 unique_policy_t unique;
578 u_int32_t rekey = 0, reauth = 0, over, jitter;
579 peer_cfg_t *peer_cfg;
580 auth_cfg_t *auth_cfg;
581
582 #ifdef ME
583 if (msg->add_conn.ikeme.mediation && msg->add_conn.ikeme.mediated_by)
584 {
585 DBG1(DBG_CFG, "a mediation connection cannot be a mediated connection "
586 "at the same time, aborting");
587 return NULL;
588 }
589
590 if (msg->add_conn.ikeme.mediation)
591 {
592 /* force unique connections for mediation connections */
593 msg->add_conn.unique = 1;
594 }
595
596 if (msg->add_conn.ikeme.mediated_by)
597 {
598 mediated_by = charon->backends->get_peer_cfg_by_name(charon->backends,
599 msg->add_conn.ikeme.mediated_by);
600 if (!mediated_by)
601 {
602 DBG1(DBG_CFG, "mediation connection '%s' not found, aborting",
603 msg->add_conn.ikeme.mediated_by);
604 return NULL;
605 }
606 if (!mediated_by->is_mediation(mediated_by))
607 {
608 DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is "
609 "no mediation connection, aborting",
610 msg->add_conn.ikeme.mediated_by, msg->add_conn.name);
611 mediated_by->destroy(mediated_by);
612 return NULL;
613 }
614 if (msg->add_conn.ikeme.peerid)
615 {
616 peer_id = identification_create_from_string(msg->add_conn.ikeme.peerid);
617 }
618 else if (msg->add_conn.other.id)
619 {
620 peer_id = identification_create_from_string(msg->add_conn.other.id);
621 }
622 }
623 #endif /* ME */
624
625 jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100;
626 over = msg->add_conn.rekey.margin;
627 if (msg->add_conn.rekey.reauth)
628 {
629 reauth = msg->add_conn.rekey.ike_lifetime - over;
630 }
631 else
632 {
633 rekey = msg->add_conn.rekey.ike_lifetime - over;
634 }
635 if (msg->add_conn.me.sourceip_mask)
636 {
637 if (msg->add_conn.me.sourceip)
638 {
639 vip = host_create_from_string(msg->add_conn.me.sourceip, 0);
640 }
641 if (!vip)
642 { /* if it is set to something like %poolname, request an address */
643 if (msg->add_conn.me.subnets)
644 { /* use the same address as in subnet, if any */
645 if (strchr(msg->add_conn.me.subnets, '.'))
646 {
647 vip = host_create_any(AF_INET);
648 }
649 else
650 {
651 vip = host_create_any(AF_INET6);
652 }
653 }
654 else
655 {
656 if (strchr(ike_cfg->get_my_addr(ike_cfg), ':'))
657 {
658 vip = host_create_any(AF_INET6);
659 }
660 else
661 {
662 vip = host_create_any(AF_INET);
663 }
664 }
665 }
666 }
667 switch (msg->add_conn.unique)
668 {
669 case 1: /* yes */
670 case 2: /* replace */
671 unique = UNIQUE_REPLACE;
672 break;
673 case 3: /* keep */
674 unique = UNIQUE_KEEP;
675 break;
676 default: /* no */
677 unique = UNIQUE_NO;
678 break;
679 }
680 if (msg->add_conn.dpd.action == 0)
681 { /* dpdaction=none disables DPD */
682 msg->add_conn.dpd.delay = 0;
683 }
684
685 /* other.sourceip is managed in stroke_attributes. If it is set, we define
686 * the pool name as the connection name, which the attribute provider
687 * uses to serve pool addresses. */
688 peer_cfg = peer_cfg_create(msg->add_conn.name,
689 msg->add_conn.version, ike_cfg,
690 msg->add_conn.me.sendcert, unique,
691 msg->add_conn.rekey.tries, rekey, reauth, jitter, over,
692 msg->add_conn.mobike, msg->add_conn.aggressive, msg->add_conn.dpd.delay,
693 vip, msg->add_conn.other.sourceip_mask ?
694 msg->add_conn.name : msg->add_conn.other.sourceip,
695 msg->add_conn.ikeme.mediation, mediated_by, peer_id);
696
697 /* build leftauth= */
698 auth_cfg = build_auth_cfg(this, msg, TRUE, TRUE);
699 if (auth_cfg)
700 {
701 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
702 }
703 else
704 { /* we require at least one config on our side */
705 peer_cfg->destroy(peer_cfg);
706 return NULL;
707 }
708 /* build leftauth2= */
709 auth_cfg = build_auth_cfg(this, msg, TRUE, FALSE);
710 if (auth_cfg)
711 {
712 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
713 }
714 /* build rightauth= */
715 auth_cfg = build_auth_cfg(this, msg, FALSE, TRUE);
716 if (auth_cfg)
717 {
718 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
719 }
720 /* build rightauth2= */
721 auth_cfg = build_auth_cfg(this, msg, FALSE, FALSE);
722 if (auth_cfg)
723 {
724 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
725 }
726 return peer_cfg;
727 }
728
729 /**
730 * build a traffic selector from a stroke_end
731 */
732 static void add_ts(private_stroke_config_t *this,
733 stroke_end_t *end, child_cfg_t *child_cfg, bool local)
734 {
735 traffic_selector_t *ts;
736
737 if (end->tohost)
738 {
739 ts = traffic_selector_create_dynamic(end->protocol,
740 end->port ? end->port : 0, end->port ? end->port : 65535);
741 child_cfg->add_traffic_selector(child_cfg, local, ts);
742 }
743 else
744 {
745 host_t *net;
746
747 if (!end->subnets)
748 {
749 net = host_create_from_string(end->address, 0);
750 if (net)
751 {
752 ts = traffic_selector_create_from_subnet(net, 0, end->protocol,
753 end->port);
754 child_cfg->add_traffic_selector(child_cfg, local, ts);
755 }
756 }
757 else
758 {
759 char *del, *start, *bits;
760
761 start = end->subnets;
762 do
763 {
764 int intbits = 0;
765
766 del = strchr(start, ',');
767 if (del)
768 {
769 *del = '\0';
770 }
771 bits = strchr(start, '/');
772 if (bits)
773 {
774 *bits = '\0';
775 intbits = atoi(bits + 1);
776 }
777
778 net = host_create_from_string(start, 0);
779 if (net)
780 {
781 ts = traffic_selector_create_from_subnet(net, intbits,
782 end->protocol, end->port);
783 child_cfg->add_traffic_selector(child_cfg, local, ts);
784 }
785 else
786 {
787 DBG1(DBG_CFG, "invalid subnet: %s, skipped", start);
788 }
789 start = del + 1;
790 }
791 while (del);
792 }
793 }
794 }
795
796 /**
797 * map starter magic values to our action type
798 */
799 static action_t map_action(int starter_action)
800 {
801 switch (starter_action)
802 {
803 case 2: /* =hold */
804 return ACTION_ROUTE;
805 case 3: /* =restart */
806 return ACTION_RESTART;
807 default:
808 return ACTION_NONE;
809 }
810 }
811
812 /**
813 * build a child config from the stroke message
814 */
815 static child_cfg_t *build_child_cfg(private_stroke_config_t *this,
816 stroke_msg_t *msg)
817 {
818 child_cfg_t *child_cfg;
819 lifetime_cfg_t lifetime = {
820 .time = {
821 .life = msg->add_conn.rekey.ipsec_lifetime,
822 .rekey = msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
823 .jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100
824 },
825 .bytes = {
826 .life = msg->add_conn.rekey.life_bytes,
827 .rekey = msg->add_conn.rekey.life_bytes - msg->add_conn.rekey.margin_bytes,
828 .jitter = msg->add_conn.rekey.margin_bytes * msg->add_conn.rekey.fuzz / 100
829 },
830 .packets = {
831 .life = msg->add_conn.rekey.life_packets,
832 .rekey = msg->add_conn.rekey.life_packets - msg->add_conn.rekey.margin_packets,
833 .jitter = msg->add_conn.rekey.margin_packets * msg->add_conn.rekey.fuzz / 100
834 }
835 };
836 mark_t mark_in = {
837 .value = msg->add_conn.mark_in.value,
838 .mask = msg->add_conn.mark_in.mask
839 };
840 mark_t mark_out = {
841 .value = msg->add_conn.mark_out.value,
842 .mask = msg->add_conn.mark_out.mask
843 };
844
845 child_cfg = child_cfg_create(
846 msg->add_conn.name, &lifetime, msg->add_conn.me.updown,
847 msg->add_conn.me.hostaccess, msg->add_conn.mode, ACTION_NONE,
848 map_action(msg->add_conn.dpd.action),
849 map_action(msg->add_conn.close_action), msg->add_conn.ipcomp,
850 msg->add_conn.inactivity, msg->add_conn.reqid,
851 &mark_in, &mark_out, msg->add_conn.tfc);
852 child_cfg->set_mipv6_options(child_cfg, msg->add_conn.proxy_mode,
853 msg->add_conn.install_policy);
854 add_ts(this, &msg->add_conn.me, child_cfg, TRUE);
855 add_ts(this, &msg->add_conn.other, child_cfg, FALSE);
856
857 add_proposals(this, msg->add_conn.algorithms.esp, NULL, child_cfg);
858
859 return child_cfg;
860 }
861
862 METHOD(stroke_config_t, add, void,
863 private_stroke_config_t *this, stroke_msg_t *msg)
864 {
865 ike_cfg_t *ike_cfg, *existing_ike;
866 peer_cfg_t *peer_cfg, *existing;
867 child_cfg_t *child_cfg;
868 enumerator_t *enumerator;
869 bool use_existing = FALSE;
870
871 ike_cfg = build_ike_cfg(this, msg);
872 if (!ike_cfg)
873 {
874 return;
875 }
876 peer_cfg = build_peer_cfg(this, msg, ike_cfg);
877 if (!peer_cfg)
878 {
879 ike_cfg->destroy(ike_cfg);
880 return;
881 }
882
883 enumerator = create_peer_cfg_enumerator(this, NULL, NULL);
884 while (enumerator->enumerate(enumerator, &existing))
885 {
886 existing_ike = existing->get_ike_cfg(existing);
887 if (existing->equals(existing, peer_cfg) &&
888 existing_ike->equals(existing_ike, peer_cfg->get_ike_cfg(peer_cfg)))
889 {
890 use_existing = TRUE;
891 peer_cfg->destroy(peer_cfg);
892 peer_cfg = existing;
893 peer_cfg->get_ref(peer_cfg);
894 DBG1(DBG_CFG, "added child to existing configuration '%s'",
895 peer_cfg->get_name(peer_cfg));
896 break;
897 }
898 }
899 enumerator->destroy(enumerator);
900
901 child_cfg = build_child_cfg(this, msg);
902 if (!child_cfg)
903 {
904 peer_cfg->destroy(peer_cfg);
905 return;
906 }
907 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
908
909 if (use_existing)
910 {
911 peer_cfg->destroy(peer_cfg);
912 }
913 else
914 {
915 /* add config to backend */
916 DBG1(DBG_CFG, "added configuration '%s'", msg->add_conn.name);
917 this->mutex->lock(this->mutex);
918 this->list->insert_last(this->list, peer_cfg);
919 this->mutex->unlock(this->mutex);
920 }
921 }
922
923 METHOD(stroke_config_t, del, void,
924 private_stroke_config_t *this, stroke_msg_t *msg)
925 {
926 enumerator_t *enumerator, *children;
927 peer_cfg_t *peer;
928 child_cfg_t *child;
929 bool deleted = FALSE;
930
931 this->mutex->lock(this->mutex);
932 enumerator = this->list->create_enumerator(this->list);
933 while (enumerator->enumerate(enumerator, (void**)&peer))
934 {
935 bool keep = FALSE;
936
937 /* remove any child with such a name */
938 children = peer->create_child_cfg_enumerator(peer);
939 while (children->enumerate(children, &child))
940 {
941 if (streq(child->get_name(child), msg->del_conn.name))
942 {
943 peer->remove_child_cfg(peer, children);
944 child->destroy(child);
945 deleted = TRUE;
946 }
947 else
948 {
949 keep = TRUE;
950 }
951 }
952 children->destroy(children);
953
954 /* if peer config matches, or has no children anymore, remove it */
955 if (!keep || streq(peer->get_name(peer), msg->del_conn.name))
956 {
957 this->list->remove_at(this->list, enumerator);
958 peer->destroy(peer);
959 deleted = TRUE;
960 }
961 }
962 enumerator->destroy(enumerator);
963 this->mutex->unlock(this->mutex);
964
965 if (deleted)
966 {
967 DBG1(DBG_CFG, "deleted connection '%s'", msg->del_conn.name);
968 }
969 else
970 {
971 DBG1(DBG_CFG, "connection '%s' not found", msg->del_conn.name);
972 }
973 }
974
975 METHOD(stroke_config_t, destroy, void,
976 private_stroke_config_t *this)
977 {
978 this->list->destroy_offset(this->list, offsetof(peer_cfg_t, destroy));
979 this->mutex->destroy(this->mutex);
980 free(this);
981 }
982
983 /*
984 * see header file
985 */
986 stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred)
987 {
988 private_stroke_config_t *this;
989
990 INIT(this,
991 .public = {
992 .backend = {
993 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
994 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
995 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
996 },
997 .add = _add,
998 .del = _del,
999 .destroy = _destroy,
1000 },
1001 .list = linked_list_create(),
1002 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
1003 .ca = ca,
1004 .cred = cred,
1005 );
1006
1007 return &this->public;
1008 }
1009