Added support for trustchain key strength checking to rightauth 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 }
332 }
333 else
334 { /* "rightauth" not defined, fall back to deprecated "eap" */
335 if (msg->add_conn.eap_type)
336 {
337 if (msg->add_conn.eap_vendor)
338 {
339 snprintf(eap_buf, sizeof(eap_buf), "eap-%d-%d",
340 msg->add_conn.eap_type,
341 msg->add_conn.eap_vendor);
342 }
343 else
344 {
345 snprintf(eap_buf, sizeof(eap_buf), "eap-%d",
346 msg->add_conn.eap_type);
347 }
348 auth = eap_buf;
349 }
350 else
351 { /* not EAP => no constraints for this peer */
352 auth = "any";
353 }
354 }
355 }
356 else
357 { /* no second authentication round, fine. But load certificates
358 * for other purposes (EAP-TLS) */
359 if (cert)
360 {
361 certificate = this->cred->load_peer(this->cred, cert);
362 if (certificate)
363 {
364 certificate->destroy(certificate);
365 }
366 }
367 return NULL;
368 }
369 }
370
371 cfg = auth_cfg_create();
372
373 /* add identity and peer certifcate */
374 identity = identification_create_from_string(id);
375 if (cert)
376 {
377 certificate = this->cred->load_peer(this->cred, cert);
378 if (certificate)
379 {
380 if (local)
381 {
382 this->ca->check_for_hash_and_url(this->ca, certificate);
383 }
384 cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate);
385 if (identity->get_type(identity) == ID_ANY ||
386 !certificate->has_subject(certificate, identity))
387 {
388 DBG1(DBG_CFG, " id '%Y' not confirmed by certificate, "
389 "defaulting to '%Y'", identity,
390 certificate->get_subject(certificate));
391 identity->destroy(identity);
392 identity = certificate->get_subject(certificate);
393 identity = identity->clone(identity);
394 }
395 }
396 }
397 cfg->add(cfg, AUTH_RULE_IDENTITY, identity);
398
399 /* CA constraint */
400 if (ca)
401 {
402 identity = identification_create_from_string(ca);
403 certificate = lib->credmgr->get_cert(lib->credmgr, CERT_X509,
404 KEY_ANY, identity, TRUE);
405 identity->destroy(identity);
406 if (certificate)
407 {
408 cfg->add(cfg, AUTH_RULE_CA_CERT, certificate);
409 }
410 else
411 {
412 DBG1(DBG_CFG, "CA certificate %s not found, discarding CA "
413 "constraint", ca);
414 }
415 }
416
417 /* groups */
418 if (end->groups)
419 {
420 enumerator_t *enumerator;
421 char *group;
422
423 enumerator = enumerator_create_token(end->groups, ",", " ");
424 while (enumerator->enumerate(enumerator, &group))
425 {
426 cfg->add(cfg, AUTH_RULE_GROUP,
427 identification_create_from_string(group));
428 }
429 enumerator->destroy(enumerator);
430 }
431
432 /* certificatePolicies */
433 if (end->cert_policy)
434 {
435 enumerator_t *enumerator;
436 char *policy;
437
438 enumerator = enumerator_create_token(end->cert_policy, ",", " ");
439 while (enumerator->enumerate(enumerator, &policy))
440 {
441 cfg->add(cfg, AUTH_RULE_CERT_POLICY, strdup(policy));
442 }
443 enumerator->destroy(enumerator);
444 }
445
446 /* authentication metod (class, actually) */
447 if (streq(auth, "pubkey") ||
448 strneq(auth, "rsa", strlen("rsa")) ||
449 strneq(auth, "ecdsa", strlen("ecdsa")))
450 {
451 u_int strength;
452
453 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
454 build_crl_policy(cfg, local, msg->add_conn.crl_policy);
455
456 if (sscanf(auth, "rsa-%d", &strength) == 1)
457 {
458 cfg->add(cfg, AUTH_RULE_RSA_STRENGTH, (uintptr_t)strength);
459 }
460 if (sscanf(auth, "ecdsa-%d", &strength) == 1)
461 {
462 cfg->add(cfg, AUTH_RULE_ECDSA_STRENGTH, (uintptr_t)strength);
463 }
464 }
465 else if (streq(auth, "psk") || streq(auth, "secret"))
466 {
467 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
468 }
469 else if (strneq(auth, "eap", 3))
470 {
471 enumerator_t *enumerator;
472 char *str;
473 int i = 0, type = 0, vendor;
474
475 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
476
477 /* parse EAP string, format: eap[-type[-vendor]] */
478 enumerator = enumerator_create_token(auth, "-", " ");
479 while (enumerator->enumerate(enumerator, &str))
480 {
481 switch (i)
482 {
483 case 1:
484 type = eap_type_from_string(str);
485 if (!type)
486 {
487 type = atoi(str);
488 if (!type)
489 {
490 DBG1(DBG_CFG, "unknown EAP method: %s", str);
491 break;
492 }
493 }
494 cfg->add(cfg, AUTH_RULE_EAP_TYPE, type);
495 break;
496 case 2:
497 if (type)
498 {
499 vendor = atoi(str);
500 if (vendor)
501 {
502 cfg->add(cfg, AUTH_RULE_EAP_VENDOR, vendor);
503 }
504 else
505 {
506 DBG1(DBG_CFG, "unknown EAP vendor: %s", str);
507 }
508 }
509 break;
510 default:
511 break;
512 }
513 i++;
514 }
515 enumerator->destroy(enumerator);
516
517 if (msg->add_conn.eap_identity)
518 {
519 if (streq(msg->add_conn.eap_identity, "%identity"))
520 {
521 identity = identification_create_from_encoding(ID_ANY,
522 chunk_empty);
523 }
524 else
525 {
526 identity = identification_create_from_string(
527 msg->add_conn.eap_identity);
528 }
529 cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, identity);
530 }
531 if (msg->add_conn.aaa_identity)
532 {
533 cfg->add(cfg, AUTH_RULE_AAA_IDENTITY,
534 identification_create_from_string(msg->add_conn.aaa_identity));
535 }
536 }
537 else
538 {
539 if (!streq(auth, "any"))
540 {
541 DBG1(DBG_CFG, "authentication method %s unknown, fallback to any",
542 auth);
543 }
544 build_crl_policy(cfg, local, msg->add_conn.crl_policy);
545 }
546 return cfg;
547 }
548
549 /**
550 * build a peer_cfg from a stroke msg
551 */
552 static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
553 stroke_msg_t *msg, ike_cfg_t *ike_cfg)
554 {
555 identification_t *peer_id = NULL;
556 peer_cfg_t *mediated_by = NULL;
557 host_t *vip = NULL;
558 unique_policy_t unique;
559 u_int32_t rekey = 0, reauth = 0, over, jitter;
560 peer_cfg_t *peer_cfg;
561 auth_cfg_t *auth_cfg;
562
563 #ifdef ME
564 if (msg->add_conn.ikeme.mediation && msg->add_conn.ikeme.mediated_by)
565 {
566 DBG1(DBG_CFG, "a mediation connection cannot be a mediated connection "
567 "at the same time, aborting");
568 return NULL;
569 }
570
571 if (msg->add_conn.ikeme.mediation)
572 {
573 /* force unique connections for mediation connections */
574 msg->add_conn.unique = 1;
575 }
576
577 if (msg->add_conn.ikeme.mediated_by)
578 {
579 mediated_by = charon->backends->get_peer_cfg_by_name(charon->backends,
580 msg->add_conn.ikeme.mediated_by);
581 if (!mediated_by)
582 {
583 DBG1(DBG_CFG, "mediation connection '%s' not found, aborting",
584 msg->add_conn.ikeme.mediated_by);
585 return NULL;
586 }
587 if (!mediated_by->is_mediation(mediated_by))
588 {
589 DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is "
590 "no mediation connection, aborting",
591 msg->add_conn.ikeme.mediated_by, msg->add_conn.name);
592 mediated_by->destroy(mediated_by);
593 return NULL;
594 }
595 if (msg->add_conn.ikeme.peerid)
596 {
597 peer_id = identification_create_from_string(msg->add_conn.ikeme.peerid);
598 }
599 else if (msg->add_conn.other.id)
600 {
601 peer_id = identification_create_from_string(msg->add_conn.other.id);
602 }
603 }
604 #endif /* ME */
605
606 jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100;
607 over = msg->add_conn.rekey.margin;
608 if (msg->add_conn.rekey.reauth)
609 {
610 reauth = msg->add_conn.rekey.ike_lifetime - over;
611 }
612 else
613 {
614 rekey = msg->add_conn.rekey.ike_lifetime - over;
615 }
616 if (msg->add_conn.me.sourceip_mask)
617 {
618 if (msg->add_conn.me.sourceip)
619 {
620 vip = host_create_from_string(msg->add_conn.me.sourceip, 0);
621 }
622 if (!vip)
623 { /* if it is set to something like %poolname, request an address */
624 if (msg->add_conn.me.subnets)
625 { /* use the same address as in subnet, if any */
626 if (strchr(msg->add_conn.me.subnets, '.'))
627 {
628 vip = host_create_any(AF_INET);
629 }
630 else
631 {
632 vip = host_create_any(AF_INET6);
633 }
634 }
635 else
636 {
637 if (strchr(ike_cfg->get_my_addr(ike_cfg), ':'))
638 {
639 vip = host_create_any(AF_INET6);
640 }
641 else
642 {
643 vip = host_create_any(AF_INET);
644 }
645 }
646 }
647 }
648 switch (msg->add_conn.unique)
649 {
650 case 1: /* yes */
651 case 2: /* replace */
652 unique = UNIQUE_REPLACE;
653 break;
654 case 3: /* keep */
655 unique = UNIQUE_KEEP;
656 break;
657 default: /* no */
658 unique = UNIQUE_NO;
659 break;
660 }
661 if (msg->add_conn.dpd.action == 0)
662 { /* dpdaction=none disables DPD */
663 msg->add_conn.dpd.delay = 0;
664 }
665
666 /* other.sourceip is managed in stroke_attributes. If it is set, we define
667 * the pool name as the connection name, which the attribute provider
668 * uses to serve pool addresses. */
669 peer_cfg = peer_cfg_create(msg->add_conn.name,
670 msg->add_conn.ikev2 ? 2 : 1, ike_cfg,
671 msg->add_conn.me.sendcert, unique,
672 msg->add_conn.rekey.tries, rekey, reauth, jitter, over,
673 msg->add_conn.mobike, msg->add_conn.dpd.delay,
674 vip, msg->add_conn.other.sourceip_mask ?
675 msg->add_conn.name : msg->add_conn.other.sourceip,
676 msg->add_conn.ikeme.mediation, mediated_by, peer_id);
677
678 /* build leftauth= */
679 auth_cfg = build_auth_cfg(this, msg, TRUE, TRUE);
680 if (auth_cfg)
681 {
682 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
683 }
684 else
685 { /* we require at least one config on our side */
686 peer_cfg->destroy(peer_cfg);
687 return NULL;
688 }
689 /* build leftauth2= */
690 auth_cfg = build_auth_cfg(this, msg, TRUE, FALSE);
691 if (auth_cfg)
692 {
693 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
694 }
695 /* build rightauth= */
696 auth_cfg = build_auth_cfg(this, msg, FALSE, TRUE);
697 if (auth_cfg)
698 {
699 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
700 }
701 /* build rightauth2= */
702 auth_cfg = build_auth_cfg(this, msg, FALSE, FALSE);
703 if (auth_cfg)
704 {
705 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
706 }
707 return peer_cfg;
708 }
709
710 /**
711 * build a traffic selector from a stroke_end
712 */
713 static void add_ts(private_stroke_config_t *this,
714 stroke_end_t *end, child_cfg_t *child_cfg, bool local)
715 {
716 traffic_selector_t *ts;
717
718 if (end->tohost)
719 {
720 ts = traffic_selector_create_dynamic(end->protocol,
721 end->port ? end->port : 0, end->port ? end->port : 65535);
722 child_cfg->add_traffic_selector(child_cfg, local, ts);
723 }
724 else
725 {
726 host_t *net;
727
728 if (!end->subnets)
729 {
730 net = host_create_from_string(end->address, 0);
731 if (net)
732 {
733 ts = traffic_selector_create_from_subnet(net, 0, end->protocol,
734 end->port);
735 child_cfg->add_traffic_selector(child_cfg, local, ts);
736 }
737 }
738 else
739 {
740 char *del, *start, *bits;
741
742 start = end->subnets;
743 do
744 {
745 int intbits = 0;
746
747 del = strchr(start, ',');
748 if (del)
749 {
750 *del = '\0';
751 }
752 bits = strchr(start, '/');
753 if (bits)
754 {
755 *bits = '\0';
756 intbits = atoi(bits + 1);
757 }
758
759 net = host_create_from_string(start, 0);
760 if (net)
761 {
762 ts = traffic_selector_create_from_subnet(net, intbits,
763 end->protocol, end->port);
764 child_cfg->add_traffic_selector(child_cfg, local, ts);
765 }
766 else
767 {
768 DBG1(DBG_CFG, "invalid subnet: %s, skipped", start);
769 }
770 start = del + 1;
771 }
772 while (del);
773 }
774 }
775 }
776
777 /**
778 * build a child config from the stroke message
779 */
780 static child_cfg_t *build_child_cfg(private_stroke_config_t *this,
781 stroke_msg_t *msg)
782 {
783 child_cfg_t *child_cfg;
784 action_t dpd;
785 lifetime_cfg_t lifetime = {
786 .time = {
787 .life = msg->add_conn.rekey.ipsec_lifetime,
788 .rekey = msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
789 .jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100
790 },
791 .bytes = {
792 .life = msg->add_conn.rekey.life_bytes,
793 .rekey = msg->add_conn.rekey.life_bytes - msg->add_conn.rekey.margin_bytes,
794 .jitter = msg->add_conn.rekey.margin_bytes * msg->add_conn.rekey.fuzz / 100
795 },
796 .packets = {
797 .life = msg->add_conn.rekey.life_packets,
798 .rekey = msg->add_conn.rekey.life_packets - msg->add_conn.rekey.margin_packets,
799 .jitter = msg->add_conn.rekey.margin_packets * msg->add_conn.rekey.fuzz / 100
800 }
801 };
802 mark_t mark_in = {
803 .value = msg->add_conn.mark_in.value,
804 .mask = msg->add_conn.mark_in.mask
805 };
806 mark_t mark_out = {
807 .value = msg->add_conn.mark_out.value,
808 .mask = msg->add_conn.mark_out.mask
809 };
810
811 switch (msg->add_conn.dpd.action)
812 { /* map startes magic values to our action type */
813 case 2: /* =hold */
814 dpd = ACTION_ROUTE;
815 break;
816 case 3: /* =restart */
817 dpd = ACTION_RESTART;
818 break;
819 default:
820 dpd = ACTION_NONE;
821 break;
822 }
823
824 child_cfg = child_cfg_create(
825 msg->add_conn.name, &lifetime,
826 msg->add_conn.me.updown, msg->add_conn.me.hostaccess,
827 msg->add_conn.mode, ACTION_NONE, dpd, dpd, msg->add_conn.ipcomp,
828 msg->add_conn.inactivity, msg->add_conn.reqid,
829 &mark_in, &mark_out, msg->add_conn.tfc);
830 child_cfg->set_mipv6_options(child_cfg, msg->add_conn.proxy_mode,
831 msg->add_conn.install_policy);
832 add_ts(this, &msg->add_conn.me, child_cfg, TRUE);
833 add_ts(this, &msg->add_conn.other, child_cfg, FALSE);
834
835 add_proposals(this, msg->add_conn.algorithms.esp, NULL, child_cfg);
836
837 return child_cfg;
838 }
839
840 METHOD(stroke_config_t, add, void,
841 private_stroke_config_t *this, stroke_msg_t *msg)
842 {
843 ike_cfg_t *ike_cfg, *existing_ike;
844 peer_cfg_t *peer_cfg, *existing;
845 child_cfg_t *child_cfg;
846 enumerator_t *enumerator;
847 bool use_existing = FALSE;
848
849 ike_cfg = build_ike_cfg(this, msg);
850 if (!ike_cfg)
851 {
852 return;
853 }
854 peer_cfg = build_peer_cfg(this, msg, ike_cfg);
855 if (!peer_cfg)
856 {
857 ike_cfg->destroy(ike_cfg);
858 return;
859 }
860
861 enumerator = create_peer_cfg_enumerator(this, NULL, NULL);
862 while (enumerator->enumerate(enumerator, &existing))
863 {
864 existing_ike = existing->get_ike_cfg(existing);
865 if (existing->equals(existing, peer_cfg) &&
866 existing_ike->equals(existing_ike, peer_cfg->get_ike_cfg(peer_cfg)))
867 {
868 use_existing = TRUE;
869 peer_cfg->destroy(peer_cfg);
870 peer_cfg = existing;
871 peer_cfg->get_ref(peer_cfg);
872 DBG1(DBG_CFG, "added child to existing configuration '%s'",
873 peer_cfg->get_name(peer_cfg));
874 break;
875 }
876 }
877 enumerator->destroy(enumerator);
878
879 child_cfg = build_child_cfg(this, msg);
880 if (!child_cfg)
881 {
882 peer_cfg->destroy(peer_cfg);
883 return;
884 }
885 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
886
887 if (use_existing)
888 {
889 peer_cfg->destroy(peer_cfg);
890 }
891 else
892 {
893 /* add config to backend */
894 DBG1(DBG_CFG, "added configuration '%s'", msg->add_conn.name);
895 this->mutex->lock(this->mutex);
896 this->list->insert_last(this->list, peer_cfg);
897 this->mutex->unlock(this->mutex);
898 }
899 }
900
901 METHOD(stroke_config_t, del, void,
902 private_stroke_config_t *this, stroke_msg_t *msg)
903 {
904 enumerator_t *enumerator, *children;
905 peer_cfg_t *peer;
906 child_cfg_t *child;
907 bool deleted = FALSE;
908
909 this->mutex->lock(this->mutex);
910 enumerator = this->list->create_enumerator(this->list);
911 while (enumerator->enumerate(enumerator, (void**)&peer))
912 {
913 bool keep = FALSE;
914
915 /* remove any child with such a name */
916 children = peer->create_child_cfg_enumerator(peer);
917 while (children->enumerate(children, &child))
918 {
919 if (streq(child->get_name(child), msg->del_conn.name))
920 {
921 peer->remove_child_cfg(peer, children);
922 child->destroy(child);
923 deleted = TRUE;
924 }
925 else
926 {
927 keep = TRUE;
928 }
929 }
930 children->destroy(children);
931
932 /* if peer config matches, or has no children anymore, remove it */
933 if (!keep || streq(peer->get_name(peer), msg->del_conn.name))
934 {
935 this->list->remove_at(this->list, enumerator);
936 peer->destroy(peer);
937 deleted = TRUE;
938 }
939 }
940 enumerator->destroy(enumerator);
941 this->mutex->unlock(this->mutex);
942
943 if (deleted)
944 {
945 DBG1(DBG_CFG, "deleted connection '%s'", msg->del_conn.name);
946 }
947 else
948 {
949 DBG1(DBG_CFG, "connection '%s' not found", msg->del_conn.name);
950 }
951 }
952
953 METHOD(stroke_config_t, destroy, void,
954 private_stroke_config_t *this)
955 {
956 this->list->destroy_offset(this->list, offsetof(peer_cfg_t, destroy));
957 this->mutex->destroy(this->mutex);
958 free(this);
959 }
960
961 /*
962 * see header file
963 */
964 stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred)
965 {
966 private_stroke_config_t *this;
967
968 INIT(this,
969 .public = {
970 .backend = {
971 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
972 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
973 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
974 },
975 .add = _add,
976 .del = _del,
977 .destroy = _destroy,
978 },
979 .list = linked_list_create(),
980 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
981 .ca = ca,
982 .cred = cred,
983 );
984
985 return &this->public;
986 }
987