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