stroke: Support public key constraints for EAP methods
[strongswan.git] / src / libcharon / plugins / stroke / stroke_config.c
1 /*
2 * Copyright (C) 2012-2014 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "stroke_config.h"
18
19 #include <hydra.h>
20 #include <daemon.h>
21 #include <threading/mutex.h>
22 #include <utils/lexparser.h>
23
24 #include <netdb.h>
25
26 typedef struct private_stroke_config_t private_stroke_config_t;
27
28 /**
29 * private data of stroke_config
30 */
31 struct private_stroke_config_t {
32
33 /**
34 * public functions
35 */
36 stroke_config_t public;
37
38 /**
39 * list of peer_cfg_t
40 */
41 linked_list_t *list;
42
43 /**
44 * mutex to lock config list
45 */
46 mutex_t *mutex;
47
48 /**
49 * ca sections
50 */
51 stroke_ca_t *ca;
52
53 /**
54 * credentials
55 */
56 stroke_cred_t *cred;
57
58 /**
59 * Virtual IP pool / DNS backend
60 */
61 stroke_attribute_t *attributes;
62 };
63
64 METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
65 private_stroke_config_t *this, identification_t *me, identification_t *other)
66 {
67 this->mutex->lock(this->mutex);
68 return enumerator_create_cleaner(this->list->create_enumerator(this->list),
69 (void*)this->mutex->unlock, this->mutex);
70 }
71
72 /**
73 * filter function for ike configs
74 */
75 static bool ike_filter(void *data, peer_cfg_t **in, ike_cfg_t **out)
76 {
77 *out = (*in)->get_ike_cfg(*in);
78 return TRUE;
79 }
80
81 METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*,
82 private_stroke_config_t *this, host_t *me, host_t *other)
83 {
84 this->mutex->lock(this->mutex);
85 return enumerator_create_filter(this->list->create_enumerator(this->list),
86 (void*)ike_filter, this->mutex,
87 (void*)this->mutex->unlock);
88 }
89
90 METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*,
91 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, protocol_id_t proto)
135 {
136 if (string)
137 {
138 char *single;
139 char *strict;
140 proposal_t *proposal;
141
142 strict = string + strlen(string) - 1;
143 if (*strict == '!')
144 {
145 *strict = '\0';
146 }
147 else
148 {
149 strict = NULL;
150 }
151 while ((single = strsep(&string, ",")))
152 {
153 proposal = proposal_create_from_string(proto, single);
154 if (proposal)
155 {
156 if (ike_cfg)
157 {
158 ike_cfg->add_proposal(ike_cfg, proposal);
159 }
160 else
161 {
162 child_cfg->add_proposal(child_cfg, proposal);
163 }
164 continue;
165 }
166 DBG1(DBG_CFG, "skipped invalid proposal string: %s", single);
167 }
168 if (strict)
169 {
170 return;
171 }
172 /* add default porposal to the end if not strict */
173 }
174 if (ike_cfg)
175 {
176 ike_cfg->add_proposal(ike_cfg, proposal_create_default(proto));
177 ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(proto));
178 }
179 else
180 {
181 child_cfg->add_proposal(child_cfg, proposal_create_default(proto));
182 child_cfg->add_proposal(child_cfg, proposal_create_default_aead(proto));
183 }
184 }
185
186 /**
187 * Build an IKE config from a stroke message
188 */
189 static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg)
190 {
191 enumerator_t *enumerator;
192 stroke_end_t tmp_end;
193 ike_cfg_t *ike_cfg;
194 host_t *host;
195 u_int16_t ikeport;
196 char me[256], other[256], *token;
197 bool swapped = FALSE;;
198
199 enumerator = enumerator_create_token(msg->add_conn.other.address, ",", " ");
200 while (enumerator->enumerate(enumerator, &token))
201 {
202 if (!strchr(token, '/'))
203 {
204 host = host_create_from_dns(token, 0, 0);
205 if (host)
206 {
207 if (hydra->kernel_interface->get_interface(
208 hydra->kernel_interface, host, NULL))
209 {
210 DBG2(DBG_CFG, "left is other host, swapping ends");
211 tmp_end = msg->add_conn.me;
212 msg->add_conn.me = msg->add_conn.other;
213 msg->add_conn.other = tmp_end;
214 swapped = TRUE;
215 }
216 host->destroy(host);
217 }
218 }
219 }
220 enumerator->destroy(enumerator);
221
222 if (!swapped)
223 {
224 enumerator = enumerator_create_token(msg->add_conn.me.address, ",", " ");
225 while (enumerator->enumerate(enumerator, &token))
226 {
227 if (!strchr(token, '/'))
228 {
229 host = host_create_from_dns(token, 0, 0);
230 if (host)
231 {
232 if (!hydra->kernel_interface->get_interface(
233 hydra->kernel_interface, host, NULL))
234 {
235 DBG1(DBG_CFG, "left nor right host is our side, "
236 "assuming left=local");
237 }
238 host->destroy(host);
239 }
240 }
241 }
242 enumerator->destroy(enumerator);
243 }
244
245 if (msg->add_conn.me.allow_any)
246 {
247 snprintf(me, sizeof(me), "%s,0.0.0.0/0,::/0",
248 msg->add_conn.me.address);
249 }
250 if (msg->add_conn.other.allow_any)
251 {
252 snprintf(other, sizeof(other), "%s,0.0.0.0/0,::/0",
253 msg->add_conn.other.address);
254 }
255 ikeport = msg->add_conn.me.ikeport;
256 ikeport = (ikeport == IKEV2_UDP_PORT) ?
257 charon->socket->get_port(charon->socket, FALSE) : ikeport;
258 ike_cfg = ike_cfg_create(msg->add_conn.version,
259 msg->add_conn.other.sendcert != CERT_NEVER_SEND,
260 msg->add_conn.force_encap,
261 msg->add_conn.me.allow_any ?
262 me : msg->add_conn.me.address,
263 ikeport,
264 msg->add_conn.other.allow_any ?
265 other : msg->add_conn.other.address,
266 msg->add_conn.other.ikeport,
267 msg->add_conn.fragmentation,
268 msg->add_conn.ikedscp);
269
270 add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL, PROTO_IKE);
271 return ike_cfg;
272 }
273
274 /**
275 * Add CRL constraint to config
276 */
277 static void build_crl_policy(auth_cfg_t *cfg, bool local, int policy)
278 {
279 /* CRL/OCSP policy, for remote config only */
280 if (!local)
281 {
282 switch (policy)
283 {
284 case CRL_STRICT_YES:
285 /* if yes, we require a GOOD validation */
286 cfg->add(cfg, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
287 break;
288 case CRL_STRICT_IFURI:
289 /* for ifuri, a SKIPPED validation is sufficient */
290 cfg->add(cfg, AUTH_RULE_CRL_VALIDATION, VALIDATION_SKIPPED);
291 break;
292 default:
293 break;
294 }
295 }
296 }
297
298 /**
299 * Parse public key / signature strength constraints
300 */
301 static void parse_pubkey_constraints(char *auth, auth_cfg_t *cfg)
302 {
303 enumerator_t *enumerator;
304 bool rsa = FALSE, ecdsa = FALSE, rsa_len = FALSE, ecdsa_len = FALSE;
305 int strength;
306 char *token;
307
308 enumerator = enumerator_create_token(auth, "-", "");
309 while (enumerator->enumerate(enumerator, &token))
310 {
311 bool found = FALSE;
312 int i;
313 struct {
314 char *name;
315 signature_scheme_t scheme;
316 key_type_t key;
317 } schemes[] = {
318 { "md5", SIGN_RSA_EMSA_PKCS1_MD5, KEY_RSA, },
319 { "sha1", SIGN_RSA_EMSA_PKCS1_SHA1, KEY_RSA, },
320 { "sha224", SIGN_RSA_EMSA_PKCS1_SHA224, KEY_RSA, },
321 { "sha256", SIGN_RSA_EMSA_PKCS1_SHA256, KEY_RSA, },
322 { "sha384", SIGN_RSA_EMSA_PKCS1_SHA384, KEY_RSA, },
323 { "sha512", SIGN_RSA_EMSA_PKCS1_SHA512, KEY_RSA, },
324 { "sha1", SIGN_ECDSA_WITH_SHA1_DER, KEY_ECDSA, },
325 { "sha256", SIGN_ECDSA_WITH_SHA256_DER, KEY_ECDSA, },
326 { "sha384", SIGN_ECDSA_WITH_SHA384_DER, KEY_ECDSA, },
327 { "sha512", SIGN_ECDSA_WITH_SHA512_DER, KEY_ECDSA, },
328 { "sha256", SIGN_ECDSA_256, KEY_ECDSA, },
329 { "sha384", SIGN_ECDSA_384, KEY_ECDSA, },
330 { "sha512", SIGN_ECDSA_521, KEY_ECDSA, },
331 };
332
333 if (rsa_len || ecdsa_len)
334 { /* expecting a key strength token */
335 strength = atoi(token);
336 if (strength)
337 {
338 if (rsa_len)
339 {
340 cfg->add(cfg, AUTH_RULE_RSA_STRENGTH, (uintptr_t)strength);
341 }
342 else if (ecdsa_len)
343 {
344 cfg->add(cfg, AUTH_RULE_ECDSA_STRENGTH, (uintptr_t)strength);
345 }
346 }
347 rsa_len = ecdsa_len = FALSE;
348 if (strength)
349 {
350 continue;
351 }
352 }
353 if (streq(token, "rsa"))
354 {
355 rsa = rsa_len = TRUE;
356 continue;
357 }
358 if (streq(token, "ecdsa"))
359 {
360 ecdsa = ecdsa_len = TRUE;
361 continue;
362 }
363 if (streq(token, "pubkey"))
364 {
365 continue;
366 }
367
368 for (i = 0; i < countof(schemes); i++)
369 {
370 if (streq(schemes[i].name, token))
371 {
372 /* for each matching string, allow the scheme, if:
373 * - it is an RSA scheme, and we enforced RSA
374 * - it is an ECDSA scheme, and we enforced ECDSA
375 * - it is not a key type specific scheme
376 */
377 if ((rsa && schemes[i].key == KEY_RSA) ||
378 (ecdsa && schemes[i].key == KEY_ECDSA) ||
379 (!rsa && !ecdsa))
380 {
381 cfg->add(cfg, AUTH_RULE_SIGNATURE_SCHEME,
382 (uintptr_t)schemes[i].scheme);
383 }
384 found = TRUE;
385 }
386 }
387 if (!found)
388 {
389 DBG1(DBG_CFG, "ignoring invalid auth token: '%s'", token);
390 }
391 }
392 enumerator->destroy(enumerator);
393 }
394
395 /**
396 * build authentication config
397 */
398 static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
399 stroke_msg_t *msg, bool local, bool primary)
400 {
401 identification_t *identity;
402 certificate_t *certificate;
403 char *auth, *id, *pubkey, *cert, *ca, *groups;
404 stroke_end_t *end, *other_end;
405 auth_cfg_t *cfg;
406 bool loose = FALSE;
407
408 /* select strings */
409 if (local)
410 {
411 end = &msg->add_conn.me;
412 other_end = &msg->add_conn.other;
413 }
414 else
415 {
416 end = &msg->add_conn.other;
417 other_end = &msg->add_conn.me;
418 }
419 if (primary)
420 {
421 auth = end->auth;
422 id = end->id;
423 if (!id)
424 { /* leftid/rightid fallback to address */
425 id = end->address;
426 }
427 cert = end->cert;
428 ca = end->ca;
429 if (ca && streq(ca, "%same"))
430 {
431 ca = other_end->ca;
432 }
433 }
434 else
435 {
436 auth = end->auth2;
437 id = end->id2;
438 if (local && !id)
439 { /* leftid2 falls back to leftid */
440 id = end->id;
441 }
442 cert = end->cert2;
443 ca = end->ca2;
444 if (ca && streq(ca, "%same"))
445 {
446 ca = other_end->ca2;
447 }
448 }
449 if (id && *id == '%' && !streq(id, "%any") && !streq(id, "%any6"))
450 { /* has only an effect on rightid/2 */
451 loose = !local;
452 id++;
453 }
454
455 if (!auth)
456 {
457 if (primary)
458 {
459 auth = "pubkey";
460 }
461 else
462 { /* no second authentication round, fine. But load certificates
463 * for other purposes (EAP-TLS) */
464 if (cert)
465 {
466 certificate = this->cred->load_peer(this->cred, cert);
467 if (certificate)
468 {
469 certificate->destroy(certificate);
470 }
471 }
472 return NULL;
473 }
474 }
475
476 cfg = auth_cfg_create();
477
478 /* add identity and peer certificate */
479 identity = identification_create_from_string(id);
480 if (cert)
481 {
482 enumerator_t *enumerator;
483 bool has_subject = FALSE;
484 certificate_t *first = NULL;
485
486 enumerator = enumerator_create_token(cert, ",", " ");
487 while (enumerator->enumerate(enumerator, &cert))
488 {
489 certificate = this->cred->load_peer(this->cred, cert);
490 if (certificate)
491 {
492 if (local)
493 {
494 this->ca->check_for_hash_and_url(this->ca, certificate);
495 }
496 cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate);
497 if (!first)
498 {
499 first = certificate;
500 }
501 if (identity->get_type(identity) != ID_ANY &&
502 certificate->has_subject(certificate, identity))
503 {
504 has_subject = TRUE;
505 }
506 }
507 }
508 enumerator->destroy(enumerator);
509
510 if (first && !has_subject)
511 {
512 DBG1(DBG_CFG, " id '%Y' not confirmed by certificate, "
513 "defaulting to '%Y'", identity, first->get_subject(first));
514 identity->destroy(identity);
515 identity = first->get_subject(first);
516 identity = identity->clone(identity);
517 }
518 }
519 /* add raw RSA public key */
520 pubkey = end->rsakey;
521 if (pubkey && !streq(pubkey, "") && !streq(pubkey, "%cert"))
522 {
523 certificate = this->cred->load_pubkey(this->cred, pubkey, identity);
524 if (certificate)
525 {
526 cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate);
527 }
528 }
529 if (identity->get_type(identity) != ID_ANY)
530 {
531 cfg->add(cfg, AUTH_RULE_IDENTITY, identity);
532 if (loose)
533 {
534 cfg->add(cfg, AUTH_RULE_IDENTITY_LOOSE, TRUE);
535 }
536 }
537 else
538 {
539 identity->destroy(identity);
540 }
541
542 /* CA constraint */
543 if (ca)
544 {
545 identity = identification_create_from_string(ca);
546 certificate = lib->credmgr->get_cert(lib->credmgr, CERT_X509,
547 KEY_ANY, identity, TRUE);
548 identity->destroy(identity);
549 if (certificate)
550 {
551 cfg->add(cfg, AUTH_RULE_CA_CERT, certificate);
552 }
553 else
554 {
555 DBG1(DBG_CFG, "CA certificate \"%s\" not found, discarding CA "
556 "constraint", ca);
557 }
558 }
559
560 /* groups */
561 groups = primary ? end->groups : end->groups2;
562 if (groups)
563 {
564 enumerator_t *enumerator;
565 char *group;
566
567 enumerator = enumerator_create_token(groups, ",", " ");
568 while (enumerator->enumerate(enumerator, &group))
569 {
570 cfg->add(cfg, AUTH_RULE_GROUP,
571 identification_create_from_string(group));
572 }
573 enumerator->destroy(enumerator);
574 }
575
576 /* certificatePolicies */
577 if (end->cert_policy)
578 {
579 enumerator_t *enumerator;
580 char *policy;
581
582 enumerator = enumerator_create_token(end->cert_policy, ",", " ");
583 while (enumerator->enumerate(enumerator, &policy))
584 {
585 cfg->add(cfg, AUTH_RULE_CERT_POLICY, strdup(policy));
586 }
587 enumerator->destroy(enumerator);
588 }
589
590 /* authentication metod (class, actually) */
591 if (strpfx(auth, "pubkey") ||
592 strpfx(auth, "rsa") ||
593 strpfx(auth, "ecdsa"))
594 {
595 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
596 build_crl_policy(cfg, local, msg->add_conn.crl_policy);
597
598 parse_pubkey_constraints(auth, cfg);
599 }
600 else if (streq(auth, "psk") || streq(auth, "secret"))
601 {
602 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
603 }
604 else if (strpfx(auth, "xauth"))
605 {
606 char *pos;
607
608 pos = strchr(auth, '-');
609 if (pos)
610 {
611 cfg->add(cfg, AUTH_RULE_XAUTH_BACKEND, strdup(++pos));
612 }
613 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_XAUTH);
614 if (msg->add_conn.xauth_identity)
615 {
616 cfg->add(cfg, AUTH_RULE_XAUTH_IDENTITY,
617 identification_create_from_string(msg->add_conn.xauth_identity));
618 }
619 }
620 else if (strpfx(auth, "eap"))
621 {
622 eap_vendor_type_t *type;
623 char *pos;
624
625 cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
626 /* check for public key constraints for EAP-TLS etc. */
627 pos = strchr(auth, ':');
628 if (pos)
629 {
630 *pos = 0;
631 parse_pubkey_constraints(pos + 1, cfg);
632 }
633 type = eap_vendor_type_from_string(auth);
634 if (type)
635 {
636 cfg->add(cfg, AUTH_RULE_EAP_TYPE, type->type);
637 if (type->vendor)
638 {
639 cfg->add(cfg, AUTH_RULE_EAP_VENDOR, type->vendor);
640 }
641 free(type);
642 }
643
644 if (msg->add_conn.eap_identity)
645 {
646 if (streq(msg->add_conn.eap_identity, "%identity"))
647 {
648 identity = identification_create_from_encoding(ID_ANY,
649 chunk_empty);
650 }
651 else
652 {
653 identity = identification_create_from_string(
654 msg->add_conn.eap_identity);
655 }
656 cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, identity);
657 }
658 if (msg->add_conn.aaa_identity)
659 {
660 cfg->add(cfg, AUTH_RULE_AAA_IDENTITY,
661 identification_create_from_string(msg->add_conn.aaa_identity));
662 }
663 }
664 else
665 {
666 if (!streq(auth, "any"))
667 {
668 DBG1(DBG_CFG, "authentication method %s unknown, fallback to any",
669 auth);
670 }
671 build_crl_policy(cfg, local, msg->add_conn.crl_policy);
672 }
673 return cfg;
674 }
675
676 /**
677 * build a mem_pool_t from an address range
678 */
679 static mem_pool_t *create_pool_range(char *str)
680 {
681 mem_pool_t *pool;
682 host_t *from, *to;
683
684 if (!host_create_from_range(str, &from, &to))
685 {
686 return NULL;
687 }
688 pool = mem_pool_create_range(str, from, to);
689 from->destroy(from);
690 to->destroy(to);
691 return pool;
692 }
693
694 /**
695 * build a peer_cfg from a stroke msg
696 */
697 static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
698 stroke_msg_t *msg, ike_cfg_t *ike_cfg)
699 {
700 identification_t *peer_id = NULL;
701 peer_cfg_t *mediated_by = NULL;
702 unique_policy_t unique;
703 u_int32_t rekey = 0, reauth = 0, over, jitter;
704 peer_cfg_t *peer_cfg;
705 auth_cfg_t *auth_cfg;
706
707 #ifdef ME
708 if (msg->add_conn.ikeme.mediation && msg->add_conn.ikeme.mediated_by)
709 {
710 DBG1(DBG_CFG, "a mediation connection cannot be a mediated connection "
711 "at the same time, aborting");
712 return NULL;
713 }
714
715 if (msg->add_conn.ikeme.mediation)
716 {
717 /* force unique connections for mediation connections */
718 msg->add_conn.unique = 1;
719 }
720
721 if (msg->add_conn.ikeme.mediated_by)
722 {
723 mediated_by = charon->backends->get_peer_cfg_by_name(charon->backends,
724 msg->add_conn.ikeme.mediated_by);
725 if (!mediated_by)
726 {
727 DBG1(DBG_CFG, "mediation connection '%s' not found, aborting",
728 msg->add_conn.ikeme.mediated_by);
729 return NULL;
730 }
731 if (!mediated_by->is_mediation(mediated_by))
732 {
733 DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is "
734 "no mediation connection, aborting",
735 msg->add_conn.ikeme.mediated_by, msg->add_conn.name);
736 mediated_by->destroy(mediated_by);
737 return NULL;
738 }
739 if (msg->add_conn.ikeme.peerid)
740 {
741 peer_id = identification_create_from_string(msg->add_conn.ikeme.peerid);
742 }
743 else if (msg->add_conn.other.id)
744 {
745 peer_id = identification_create_from_string(msg->add_conn.other.id);
746 }
747 }
748 #endif /* ME */
749
750 jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100;
751 over = msg->add_conn.rekey.margin;
752 if (msg->add_conn.rekey.reauth)
753 {
754 reauth = msg->add_conn.rekey.ike_lifetime - over;
755 }
756 else
757 {
758 rekey = msg->add_conn.rekey.ike_lifetime - over;
759 }
760 switch (msg->add_conn.unique)
761 {
762 case 1: /* yes */
763 case 2: /* replace */
764 unique = UNIQUE_REPLACE;
765 break;
766 case 3: /* keep */
767 unique = UNIQUE_KEEP;
768 break;
769 case 4: /* never */
770 unique = UNIQUE_NEVER;
771 break;
772 default: /* no */
773 unique = UNIQUE_NO;
774 break;
775 }
776 if (msg->add_conn.dpd.action == 0)
777 { /* dpdaction=none disables DPD */
778 msg->add_conn.dpd.delay = 0;
779 }
780
781 /* other.sourceip is managed in stroke_attributes. If it is set, we define
782 * the pool name as the connection name, which the attribute provider
783 * uses to serve pool addresses. */
784 peer_cfg = peer_cfg_create(msg->add_conn.name, ike_cfg,
785 msg->add_conn.me.sendcert, unique,
786 msg->add_conn.rekey.tries, rekey, reauth, jitter, over,
787 msg->add_conn.mobike, msg->add_conn.aggressive,
788 msg->add_conn.pushmode == 0,
789 msg->add_conn.dpd.delay, msg->add_conn.dpd.timeout,
790 msg->add_conn.ikeme.mediation, mediated_by, peer_id);
791
792 if (msg->add_conn.other.sourceip)
793 {
794 enumerator_t *enumerator;
795 char *token;
796
797 enumerator = enumerator_create_token(msg->add_conn.other.sourceip,
798 ",", " ");
799 while (enumerator->enumerate(enumerator, &token))
800 {
801 if (streq(token, "%modeconfig") || streq(token, "%modecfg") ||
802 streq(token, "%config") || streq(token, "%cfg") ||
803 streq(token, "%config4") || streq(token, "%config6"))
804 {
805 /* empty pool, uses connection name */
806 this->attributes->add_pool(this->attributes,
807 mem_pool_create(msg->add_conn.name, NULL, 0));
808 peer_cfg->add_pool(peer_cfg, msg->add_conn.name);
809 }
810 else if (*token == '%')
811 {
812 /* external named pool */
813 peer_cfg->add_pool(peer_cfg, token + 1);
814 }
815 else
816 {
817 /* in-memory pool, using range or CIDR notation */
818 mem_pool_t *pool;
819 host_t *base;
820 int bits;
821
822 pool = create_pool_range(token);
823 if (!pool)
824 {
825 base = host_create_from_subnet(token, &bits);
826 if (base)
827 {
828 pool = mem_pool_create(token, base, bits);
829 base->destroy(base);
830 }
831 }
832 if (pool)
833 {
834 this->attributes->add_pool(this->attributes, pool);
835 peer_cfg->add_pool(peer_cfg, token);
836 }
837 else
838 {
839 DBG1(DBG_CFG, "IP pool %s invalid, ignored", token);
840 }
841 }
842 }
843 enumerator->destroy(enumerator);
844 }
845
846 if (msg->add_conn.me.sourceip && msg->add_conn.other.sourceip)
847 {
848 DBG1(DBG_CFG, "'%s' has both left- and rightsourceip, but IKE can "
849 "negotiate one virtual IP only, ignoring local virtual IP",
850 msg->add_conn.name);
851 }
852 else if (msg->add_conn.me.sourceip)
853 {
854 enumerator_t *enumerator;
855 char *token;
856
857 enumerator = enumerator_create_token(msg->add_conn.me.sourceip, ",", " ");
858 while (enumerator->enumerate(enumerator, &token))
859 {
860 host_t *vip = NULL;
861
862 if (streq(token, "%modeconfig") || streq(token, "%modecfg") ||
863 streq(token, "%config") || streq(token, "%cfg"))
864 { /* try to deduce an address family */
865 if (msg->add_conn.me.subnets)
866 { /* use the same family as in local subnet, if any */
867 if (strchr(msg->add_conn.me.subnets, '.'))
868 {
869 vip = host_create_any(AF_INET);
870 }
871 else
872 {
873 vip = host_create_any(AF_INET6);
874 }
875 }
876 else if (msg->add_conn.other.subnets)
877 { /* use the same family as in remote subnet, if any */
878 if (strchr(msg->add_conn.other.subnets, '.'))
879 {
880 vip = host_create_any(AF_INET);
881 }
882 else
883 {
884 vip = host_create_any(AF_INET6);
885 }
886 }
887 else
888 {
889 char *addr, *next, *hit;
890
891 /* guess virtual IP family based on local address. If
892 * multiple addresses are specified, we look at the first
893 * only, as with leftallowany a ::/0 is always appended. */
894 addr = ike_cfg->get_my_addr(ike_cfg);
895 next = strchr(addr, ',');
896 hit = strchr(addr, ':');
897 if (hit && (!next || hit < next))
898 {
899 vip = host_create_any(AF_INET6);
900 }
901 else
902 {
903 vip = host_create_any(AF_INET);
904 }
905 }
906 }
907 else if (streq(token, "%config4"))
908 {
909 vip = host_create_any(AF_INET);
910 }
911 else if (streq(token, "%config6"))
912 {
913 vip = host_create_any(AF_INET6);
914 }
915 else
916 {
917 vip = host_create_from_string(token, 0);
918 if (!vip)
919 {
920 DBG1(DBG_CFG, "ignored invalid subnet token: %s", token);
921 }
922 }
923
924 if (vip)
925 {
926 peer_cfg->add_virtual_ip(peer_cfg, vip);
927 }
928 }
929 enumerator->destroy(enumerator);
930 }
931
932 /* build leftauth= */
933 auth_cfg = build_auth_cfg(this, msg, TRUE, TRUE);
934 if (auth_cfg)
935 {
936 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
937 }
938 else
939 { /* we require at least one config on our side */
940 peer_cfg->destroy(peer_cfg);
941 return NULL;
942 }
943 /* build leftauth2= */
944 auth_cfg = build_auth_cfg(this, msg, TRUE, FALSE);
945 if (auth_cfg)
946 {
947 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
948 }
949 /* build rightauth= */
950 auth_cfg = build_auth_cfg(this, msg, FALSE, TRUE);
951 if (auth_cfg)
952 {
953 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
954 }
955 /* build rightauth2= */
956 auth_cfg = build_auth_cfg(this, msg, FALSE, FALSE);
957 if (auth_cfg)
958 {
959 peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
960 }
961 return peer_cfg;
962 }
963
964 /**
965 * Parse a protoport specifier
966 */
967 static bool parse_protoport(char *token, u_int16_t *from_port,
968 u_int16_t *to_port, u_int8_t *protocol)
969 {
970 char *sep, *port = "", *endptr;
971 struct protoent *proto;
972 struct servent *svc;
973 long int p;
974
975 sep = strrchr(token, ']');
976 if (!sep)
977 {
978 return FALSE;
979 }
980 *sep = '\0';
981
982 sep = strchr(token, '/');
983 if (sep)
984 { /* protocol/port */
985 *sep = '\0';
986 port = sep + 1;
987 }
988
989 if (streq(token, "%any"))
990 {
991 *protocol = 0;
992 }
993 else
994 {
995 proto = getprotobyname(token);
996 if (proto)
997 {
998 *protocol = proto->p_proto;
999 }
1000 else
1001 {
1002 p = strtol(token, &endptr, 0);
1003 if ((*token && *endptr) || p < 0 || p > 0xff)
1004 {
1005 return FALSE;
1006 }
1007 *protocol = (u_int8_t)p;
1008 }
1009 }
1010 if (streq(port, "%any"))
1011 {
1012 *from_port = 0;
1013 *to_port = 0xffff;
1014 }
1015 else if (streq(port, "%opaque"))
1016 {
1017 *from_port = 0xffff;
1018 *to_port = 0;
1019 }
1020 else if (*port)
1021 {
1022 svc = getservbyname(port, NULL);
1023 if (svc)
1024 {
1025 *from_port = *to_port = ntohs(svc->s_port);
1026 }
1027 else
1028 {
1029 p = strtol(port, &endptr, 0);
1030 if (p < 0 || p > 0xffff)
1031 {
1032 return FALSE;
1033 }
1034 *from_port = p;
1035 if (*endptr == '-')
1036 {
1037 port = endptr + 1;
1038 p = strtol(port, &endptr, 0);
1039 if (p < 0 || p > 0xffff)
1040 {
1041 return FALSE;
1042 }
1043 }
1044 *to_port = p;
1045 if (*endptr)
1046 {
1047 return FALSE;
1048 }
1049 }
1050 }
1051 return TRUE;
1052 }
1053
1054 /**
1055 * build a traffic selector from a stroke_end
1056 */
1057 static void add_ts(private_stroke_config_t *this,
1058 stroke_end_t *end, child_cfg_t *child_cfg, bool local)
1059 {
1060 traffic_selector_t *ts;
1061
1062 if (end->tohost)
1063 {
1064 ts = traffic_selector_create_dynamic(end->protocol,
1065 end->from_port, end->to_port);
1066 child_cfg->add_traffic_selector(child_cfg, local, ts);
1067 }
1068 else
1069 {
1070 if (!end->subnets)
1071 {
1072 host_t *net;
1073
1074 net = host_create_from_string(end->address, 0);
1075 if (net)
1076 {
1077 ts = traffic_selector_create_from_subnet(net, 0, end->protocol,
1078 end->from_port, end->to_port);
1079 child_cfg->add_traffic_selector(child_cfg, local, ts);
1080 }
1081 }
1082 else
1083 {
1084 enumerator_t *enumerator;
1085 char *subnet, *pos;
1086 u_int16_t from_port, to_port;
1087 u_int8_t proto;
1088
1089 enumerator = enumerator_create_token(end->subnets, ",", " ");
1090 while (enumerator->enumerate(enumerator, &subnet))
1091 {
1092 from_port = end->from_port;
1093 to_port = end->to_port;
1094 proto = end->protocol;
1095
1096 pos = strchr(subnet, '[');
1097 if (pos)
1098 {
1099 *(pos++) = '\0';
1100 if (!parse_protoport(pos, &from_port, &to_port, &proto))
1101 {
1102 DBG1(DBG_CFG, "invalid proto/port: %s, skipped subnet",
1103 pos);
1104 continue;
1105 }
1106 }
1107 if (streq(subnet, "%dynamic"))
1108 {
1109 ts = traffic_selector_create_dynamic(proto,
1110 from_port, to_port);
1111 }
1112 else
1113 {
1114 ts = traffic_selector_create_from_cidr(subnet, proto,
1115 from_port, to_port);
1116 }
1117 if (ts)
1118 {
1119 child_cfg->add_traffic_selector(child_cfg, local, ts);
1120 }
1121 else
1122 {
1123 DBG1(DBG_CFG, "invalid subnet: %s, skipped", subnet);
1124 }
1125 }
1126 enumerator->destroy(enumerator);
1127 }
1128 }
1129 }
1130
1131 /**
1132 * map starter magic values to our action type
1133 */
1134 static action_t map_action(int starter_action)
1135 {
1136 switch (starter_action)
1137 {
1138 case 2: /* =hold */
1139 return ACTION_ROUTE;
1140 case 3: /* =restart */
1141 return ACTION_RESTART;
1142 default:
1143 return ACTION_NONE;
1144 }
1145 }
1146
1147 /**
1148 * build a child config from the stroke message
1149 */
1150 static child_cfg_t *build_child_cfg(private_stroke_config_t *this,
1151 stroke_msg_t *msg)
1152 {
1153 child_cfg_t *child_cfg;
1154 lifetime_cfg_t lifetime = {
1155 .time = {
1156 .life = msg->add_conn.rekey.ipsec_lifetime,
1157 .rekey = msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
1158 .jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100
1159 },
1160 .bytes = {
1161 .life = msg->add_conn.rekey.life_bytes,
1162 .rekey = msg->add_conn.rekey.life_bytes - msg->add_conn.rekey.margin_bytes,
1163 .jitter = msg->add_conn.rekey.margin_bytes * msg->add_conn.rekey.fuzz / 100
1164 },
1165 .packets = {
1166 .life = msg->add_conn.rekey.life_packets,
1167 .rekey = msg->add_conn.rekey.life_packets - msg->add_conn.rekey.margin_packets,
1168 .jitter = msg->add_conn.rekey.margin_packets * msg->add_conn.rekey.fuzz / 100
1169 }
1170 };
1171 mark_t mark_in = {
1172 .value = msg->add_conn.mark_in.value,
1173 .mask = msg->add_conn.mark_in.mask
1174 };
1175 mark_t mark_out = {
1176 .value = msg->add_conn.mark_out.value,
1177 .mask = msg->add_conn.mark_out.mask
1178 };
1179
1180 child_cfg = child_cfg_create(
1181 msg->add_conn.name, &lifetime, msg->add_conn.me.updown,
1182 msg->add_conn.me.hostaccess, msg->add_conn.mode, ACTION_NONE,
1183 map_action(msg->add_conn.dpd.action),
1184 map_action(msg->add_conn.close_action), msg->add_conn.ipcomp,
1185 msg->add_conn.inactivity, msg->add_conn.reqid,
1186 &mark_in, &mark_out, msg->add_conn.tfc);
1187 if (msg->add_conn.replay_window != -1)
1188 {
1189 child_cfg->set_replay_window(child_cfg, msg->add_conn.replay_window);
1190 }
1191 child_cfg->set_mipv6_options(child_cfg, msg->add_conn.proxy_mode,
1192 msg->add_conn.install_policy);
1193 add_ts(this, &msg->add_conn.me, child_cfg, TRUE);
1194 add_ts(this, &msg->add_conn.other, child_cfg, FALSE);
1195
1196 if (msg->add_conn.algorithms.ah)
1197 {
1198 add_proposals(this, msg->add_conn.algorithms.ah,
1199 NULL, child_cfg, PROTO_AH);
1200 }
1201 else
1202 {
1203 add_proposals(this, msg->add_conn.algorithms.esp,
1204 NULL, child_cfg, PROTO_ESP);
1205 }
1206 return child_cfg;
1207 }
1208
1209 METHOD(stroke_config_t, add, void,
1210 private_stroke_config_t *this, stroke_msg_t *msg)
1211 {
1212 ike_cfg_t *ike_cfg, *existing_ike;
1213 peer_cfg_t *peer_cfg, *existing;
1214 child_cfg_t *child_cfg;
1215 enumerator_t *enumerator;
1216 bool use_existing = FALSE;
1217
1218 ike_cfg = build_ike_cfg(this, msg);
1219 if (!ike_cfg)
1220 {
1221 return;
1222 }
1223 peer_cfg = build_peer_cfg(this, msg, ike_cfg);
1224 if (!peer_cfg)
1225 {
1226 ike_cfg->destroy(ike_cfg);
1227 return;
1228 }
1229
1230 enumerator = create_peer_cfg_enumerator(this, NULL, NULL);
1231 while (enumerator->enumerate(enumerator, &existing))
1232 {
1233 existing_ike = existing->get_ike_cfg(existing);
1234 if (existing->equals(existing, peer_cfg) &&
1235 existing_ike->equals(existing_ike, peer_cfg->get_ike_cfg(peer_cfg)))
1236 {
1237 use_existing = TRUE;
1238 peer_cfg->destroy(peer_cfg);
1239 peer_cfg = existing;
1240 peer_cfg->get_ref(peer_cfg);
1241 DBG1(DBG_CFG, "added child to existing configuration '%s'",
1242 peer_cfg->get_name(peer_cfg));
1243 break;
1244 }
1245 }
1246 enumerator->destroy(enumerator);
1247
1248 child_cfg = build_child_cfg(this, msg);
1249 if (!child_cfg)
1250 {
1251 peer_cfg->destroy(peer_cfg);
1252 return;
1253 }
1254 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
1255
1256 if (use_existing)
1257 {
1258 peer_cfg->destroy(peer_cfg);
1259 }
1260 else
1261 {
1262 /* add config to backend */
1263 DBG1(DBG_CFG, "added configuration '%s'", msg->add_conn.name);
1264 this->mutex->lock(this->mutex);
1265 this->list->insert_last(this->list, peer_cfg);
1266 this->mutex->unlock(this->mutex);
1267 }
1268 }
1269
1270 METHOD(stroke_config_t, del, void,
1271 private_stroke_config_t *this, stroke_msg_t *msg)
1272 {
1273 enumerator_t *enumerator, *children;
1274 peer_cfg_t *peer;
1275 child_cfg_t *child;
1276 bool deleted = FALSE;
1277
1278 this->mutex->lock(this->mutex);
1279 enumerator = this->list->create_enumerator(this->list);
1280 while (enumerator->enumerate(enumerator, &peer))
1281 {
1282 bool keep = FALSE;
1283
1284 /* remove any child with such a name */
1285 children = peer->create_child_cfg_enumerator(peer);
1286 while (children->enumerate(children, &child))
1287 {
1288 if (streq(child->get_name(child), msg->del_conn.name))
1289 {
1290 peer->remove_child_cfg(peer, children);
1291 child->destroy(child);
1292 deleted = TRUE;
1293 }
1294 else
1295 {
1296 keep = TRUE;
1297 }
1298 }
1299 children->destroy(children);
1300
1301 /* if peer config has no children anymore, remove it */
1302 if (!keep)
1303 {
1304 this->list->remove_at(this->list, enumerator);
1305 peer->destroy(peer);
1306 }
1307 }
1308 enumerator->destroy(enumerator);
1309 this->mutex->unlock(this->mutex);
1310
1311 if (deleted)
1312 {
1313 DBG1(DBG_CFG, "deleted connection '%s'", msg->del_conn.name);
1314 }
1315 else
1316 {
1317 DBG1(DBG_CFG, "connection '%s' not found", msg->del_conn.name);
1318 }
1319 }
1320
1321 METHOD(stroke_config_t, set_user_credentials, void,
1322 private_stroke_config_t *this, stroke_msg_t *msg, FILE *prompt)
1323 {
1324 enumerator_t *enumerator, *children, *remote_auth;
1325 peer_cfg_t *peer, *found = NULL;
1326 auth_cfg_t *auth_cfg, *remote_cfg;
1327 auth_class_t auth_class;
1328 child_cfg_t *child;
1329 identification_t *id, *identity, *gw = NULL;
1330 shared_key_type_t type = SHARED_ANY;
1331 chunk_t password = chunk_empty;
1332
1333 this->mutex->lock(this->mutex);
1334 enumerator = this->list->create_enumerator(this->list);
1335 while (enumerator->enumerate(enumerator, (void**)&peer))
1336 { /* find the peer (or child) config with the given name */
1337 if (streq(peer->get_name(peer), msg->user_creds.name))
1338 {
1339 found = peer;
1340 }
1341 else
1342 {
1343 children = peer->create_child_cfg_enumerator(peer);
1344 while (children->enumerate(children, &child))
1345 {
1346 if (streq(child->get_name(child), msg->user_creds.name))
1347 {
1348 found = peer;
1349 break;
1350 }
1351 }
1352 children->destroy(children);
1353 }
1354
1355 if (found)
1356 {
1357 break;
1358 }
1359 }
1360 enumerator->destroy(enumerator);
1361
1362 if (!found)
1363 {
1364 DBG1(DBG_CFG, " no config named '%s'", msg->user_creds.name);
1365 fprintf(prompt, "no config named '%s'\n", msg->user_creds.name);
1366 this->mutex->unlock(this->mutex);
1367 return;
1368 }
1369
1370 id = identification_create_from_string(msg->user_creds.username);
1371 if (strlen(msg->user_creds.username) == 0 ||
1372 !id || id->get_type(id) == ID_ANY)
1373 {
1374 DBG1(DBG_CFG, " invalid username '%s'", msg->user_creds.username);
1375 fprintf(prompt, "invalid username '%s'\n", msg->user_creds.username);
1376 this->mutex->unlock(this->mutex);
1377 DESTROY_IF(id);
1378 return;
1379 }
1380
1381 /* replace/set the username in the first EAP/XAuth auth_cfg, also look for
1382 * a suitable remote ID.
1383 * note that adding the identity here is not fully thread-safe as the
1384 * peer_cfg and in turn the auth_cfg could be in use. for the default use
1385 * case (setting user credentials before upping the connection) this will
1386 * not be a problem, though. */
1387 enumerator = found->create_auth_cfg_enumerator(found, TRUE);
1388 remote_auth = found->create_auth_cfg_enumerator(found, FALSE);
1389 while (enumerator->enumerate(enumerator, (void**)&auth_cfg))
1390 {
1391 if (remote_auth->enumerate(remote_auth, (void**)&remote_cfg))
1392 { /* fall back on rightid, in case aaa_identity is not specified */
1393 identity = remote_cfg->get(remote_cfg, AUTH_RULE_IDENTITY);
1394 if (identity && identity->get_type(identity) != ID_ANY)
1395 {
1396 gw = identity;
1397 }
1398 }
1399
1400 auth_class = (uintptr_t)auth_cfg->get(auth_cfg, AUTH_RULE_AUTH_CLASS);
1401 if (auth_class == AUTH_CLASS_EAP || auth_class == AUTH_CLASS_XAUTH)
1402 {
1403 if (auth_class == AUTH_CLASS_EAP)
1404 {
1405 auth_cfg->add(auth_cfg, AUTH_RULE_EAP_IDENTITY, id->clone(id));
1406 /* if aaa_identity is specified use that as remote ID */
1407 identity = auth_cfg->get(auth_cfg, AUTH_RULE_AAA_IDENTITY);
1408 if (identity && identity->get_type(identity) != ID_ANY)
1409 {
1410 gw = identity;
1411 }
1412 DBG1(DBG_CFG, " configured EAP-Identity %Y", id);
1413 }
1414 else
1415 {
1416 auth_cfg->add(auth_cfg, AUTH_RULE_XAUTH_IDENTITY,
1417 id->clone(id));
1418 DBG1(DBG_CFG, " configured XAuth username %Y", id);
1419 }
1420 type = SHARED_EAP;
1421 break;
1422 }
1423 }
1424 enumerator->destroy(enumerator);
1425 remote_auth->destroy(remote_auth);
1426 /* clone the gw ID before unlocking the mutex */
1427 if (gw)
1428 {
1429 gw = gw->clone(gw);
1430 }
1431 this->mutex->unlock(this->mutex);
1432
1433 if (type == SHARED_ANY)
1434 {
1435 DBG1(DBG_CFG, " config '%s' unsuitable for user credentials",
1436 msg->user_creds.name);
1437 fprintf(prompt, "config '%s' unsuitable for user credentials\n",
1438 msg->user_creds.name);
1439 id->destroy(id);
1440 DESTROY_IF(gw);
1441 return;
1442 }
1443
1444 if (msg->user_creds.password)
1445 {
1446 char *pass;
1447
1448 pass = msg->user_creds.password;
1449 password = chunk_clone(chunk_create(pass, strlen(pass)));
1450 memwipe(pass, strlen(pass));
1451 }
1452 else
1453 { /* prompt the user for the password */
1454 char buf[256];
1455
1456 fprintf(prompt, "Password:\n");
1457 if (fgets(buf, sizeof(buf), prompt))
1458 {
1459 password = chunk_clone(chunk_create(buf, strlen(buf)));
1460 if (password.len > 0)
1461 { /* trim trailing \n */
1462 password.len--;
1463 }
1464 memwipe(buf, sizeof(buf));
1465 }
1466 }
1467
1468 if (password.len)
1469 {
1470 shared_key_t *shared;
1471 linked_list_t *owners;
1472
1473 shared = shared_key_create(type, password);
1474
1475 owners = linked_list_create();
1476 owners->insert_last(owners, id->clone(id));
1477 if (gw && gw->get_type(gw) != ID_ANY)
1478 {
1479 owners->insert_last(owners, gw->clone(gw));
1480 DBG1(DBG_CFG, " added %N secret for %Y %Y", shared_key_type_names,
1481 type, id, gw);
1482 }
1483 else
1484 {
1485 DBG1(DBG_CFG, " added %N secret for %Y", shared_key_type_names,
1486 type, id);
1487 }
1488 this->cred->add_shared(this->cred, shared, owners);
1489 DBG4(DBG_CFG, " secret: %#B", &password);
1490 }
1491 else
1492 { /* in case a user answers the password prompt by just pressing enter */
1493 chunk_clear(&password);
1494 }
1495 id->destroy(id);
1496 DESTROY_IF(gw);
1497 }
1498
1499 METHOD(stroke_config_t, destroy, void,
1500 private_stroke_config_t *this)
1501 {
1502 this->list->destroy_offset(this->list, offsetof(peer_cfg_t, destroy));
1503 this->mutex->destroy(this->mutex);
1504 free(this);
1505 }
1506
1507 /*
1508 * see header file
1509 */
1510 stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred,
1511 stroke_attribute_t *attributes)
1512 {
1513 private_stroke_config_t *this;
1514
1515 INIT(this,
1516 .public = {
1517 .backend = {
1518 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
1519 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
1520 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
1521 },
1522 .add = _add,
1523 .del = _del,
1524 .set_user_credentials = _set_user_credentials,
1525 .destroy = _destroy,
1526 },
1527 .list = linked_list_create(),
1528 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
1529 .ca = ca,
1530 .cred = cred,
1531 .attributes = attributes,
1532 );
1533
1534 return &this->public;
1535 }