splitted stroke plugin to several files:
[strongswan.git] / src / charon / 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 * $Id$
16 */
17
18 #include "stroke_config.h"
19
20 #include <daemon.h>
21 #include <utils/mutex.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 * credentials
47 */
48 stroke_cred_t *cred;
49 };
50
51 /**
52 * data to pass peer_filter
53 */
54 typedef struct {
55 private_stroke_config_t *this;
56 identification_t *me;
57 identification_t *other;
58 } peer_data_t;
59
60 /**
61 * destroy id enumerator data and unlock list
62 */
63 static void peer_data_destroy(peer_data_t *data)
64 {
65 data->this->mutex->unlock(data->this->mutex);
66 free(data);
67 }
68
69 /**
70 * filter function for peer configs
71 */
72 static bool peer_filter(peer_data_t *data, peer_cfg_t **in, peer_cfg_t **out)
73 {
74 bool match_me = FALSE, match_other = FALSE;
75 identification_t *me, *other;
76
77 me = (*in)->get_my_id(*in);
78 other = (*in)->get_other_id(*in);
79
80 /* own ID may have wildcards in data (no IDr payload) or in config */
81 match_me = (!data->me || data->me->matches(data->me, me) ||
82 me->matches(me, data->me));
83 /* others ID has wildcards in config only */
84 match_other = (!data->other || data->other->matches(data->other, other));
85
86 if (match_me && match_other)
87 {
88 *out = *in;
89 return TRUE;
90 }
91 return FALSE;
92 }
93
94 /**
95 * Implementation of backend_t.create_peer_cfg_enumerator.
96 */
97 static enumerator_t* create_peer_cfg_enumerator(private_stroke_config_t *this,
98 identification_t *me,
99 identification_t *other)
100 {
101 peer_data_t *data;
102
103 data = malloc_thing(peer_data_t);
104 data->this = this;
105 data->me = me;
106 data->other = other;
107
108 this->mutex->lock(this->mutex);
109 return enumerator_create_filter(this->list->create_enumerator(this->list),
110 (void*)peer_filter, data,
111 (void*)peer_data_destroy);
112 }
113
114 /**
115 * data to pass ike_filter
116 */
117 typedef struct {
118 private_stroke_config_t *this;
119 host_t *me;
120 host_t *other;
121 } ike_data_t;
122
123 /**
124 * destroy id enumerator data and unlock list
125 */
126 static void ike_data_destroy(ike_data_t *data)
127 {
128 data->this->mutex->unlock(data->this->mutex);
129 free(data);
130 }
131
132 /**
133 * filter function for ike configs
134 */
135 static bool ike_filter(ike_data_t *data, peer_cfg_t **in, ike_cfg_t **out)
136 {
137 ike_cfg_t *ike_cfg;
138 host_t *me, *other;
139
140 ike_cfg = (*in)->get_ike_cfg(*in);
141
142 me = ike_cfg->get_my_host(ike_cfg);
143 other = ike_cfg->get_other_host(ike_cfg);
144 if ((!data->me || me->is_anyaddr(me) || me->ip_equals(me, data->me)) &&
145 (!data->other || other->is_anyaddr(other) || other->ip_equals(other, data->other)))
146 {
147 *out = ike_cfg;
148 return TRUE;
149 }
150 return FALSE;
151 }
152
153 /**
154 * Implementation of backend_t.create_ike_cfg_enumerator.
155 */
156 static enumerator_t* create_ike_cfg_enumerator(private_stroke_config_t *this,
157 host_t *me, host_t *other)
158 {
159 ike_data_t *data;
160
161 data = malloc_thing(ike_data_t);
162 data->this = this;
163 data->me = me;
164 data->other = other;
165
166 this->mutex->lock(this->mutex);
167 return enumerator_create_filter(this->list->create_enumerator(this->list),
168 (void*)ike_filter, data,
169 (void*)ike_data_destroy);
170 }
171
172 /**
173 * implements backend_t.get_peer_cfg_by_name.
174 */
175 static peer_cfg_t *get_peer_cfg_by_name(private_stroke_config_t *this, char *name)
176 {
177 enumerator_t *e1, *e2;
178 peer_cfg_t *current, *found = NULL;
179 child_cfg_t *child;
180
181 this->mutex->lock(this->mutex);
182 e1 = this->list->create_enumerator(this->list);
183 while (e1->enumerate(e1, &current))
184 {
185 /* compare peer_cfgs name first */
186 if (streq(current->get_name(current), name))
187 {
188 found = current;
189 found->get_ref(found);
190 break;
191 }
192 /* compare all child_cfg names otherwise */
193 e2 = current->create_child_cfg_enumerator(current);
194 while (e2->enumerate(e2, &child))
195 {
196 if (streq(child->get_name(child), name))
197 {
198 found = current;
199 found->get_ref(found);
200 break;
201 }
202 }
203 e2->destroy(e2);
204 if (found)
205 {
206 break;
207 }
208 }
209 e1->destroy(e1);
210 this->mutex->unlock(this->mutex);
211 return found;
212 }
213
214 /**
215 * check if a certificate has an ID
216 */
217 static identification_t *update_peerid(certificate_t *cert, identification_t *id)
218 {
219 if (!cert->has_subject(cert, id))
220 {
221 DBG1(DBG_CFG, " peerid %D not confirmed by certificate, "
222 "defaulting to subject DN", id);
223 id->destroy(id);
224 id = cert->get_subject(cert);
225 return id->clone(id);
226 }
227 return id;
228 }
229
230 /**
231 * parse a proposal string, either into ike_cfg or child_cfg
232 */
233 static void add_proposals(private_stroke_config_t *this, char *string,
234 ike_cfg_t *ike_cfg, child_cfg_t *child_cfg)
235 {
236 if (string)
237 {
238 char *single;
239 char *strict;
240 proposal_t *proposal;
241 protocol_id_t proto = PROTO_ESP;
242
243 if (ike_cfg)
244 {
245 proto = PROTO_IKE;
246 }
247 strict = string + strlen(string) - 1;
248 if (*strict == '!')
249 {
250 *strict = '\0';
251 }
252 else
253 {
254 strict = NULL;
255 }
256 while ((single = strsep(&string, ",")))
257 {
258 proposal = proposal_create_from_string(proto, single);
259 if (proposal)
260 {
261 if (ike_cfg)
262 {
263 ike_cfg->add_proposal(ike_cfg, proposal);
264 }
265 else
266 {
267 child_cfg->add_proposal(child_cfg, proposal);
268 }
269 continue;
270 }
271 DBG1(DBG_CFG, "skipped invalid proposal string: %s", single);
272 }
273 if (strict)
274 {
275 return;
276 }
277 /* add default porposal to the end if not strict */
278 }
279 if (ike_cfg)
280 {
281 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
282 }
283 else
284 {
285 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
286 }
287 }
288
289 /**
290 * Build an IKE config from a stroke message
291 */
292 static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg)
293 {
294 host_t *me = NULL, *other = NULL, *tmp;
295 stroke_end_t tmp_end;
296 ike_cfg_t *ike_cfg;
297 char *interface;
298
299 if (msg->add_conn.me.address)
300 {
301 me = host_create_from_string(msg->add_conn.me.address, IKEV2_UDP_PORT);
302 }
303 if (!me)
304 {
305 DBG1(DBG_CFG, "invalid left host: %s", msg->add_conn.me.address);
306 return NULL;
307 }
308 if (msg->add_conn.other.address)
309 {
310 other = host_create_from_string(msg->add_conn.other.address, IKEV2_UDP_PORT);
311 }
312 if (!other)
313 {
314 DBG1(DBG_CFG, "invalid right host: %s", msg->add_conn.other.address);
315 me->destroy(me);
316 return NULL;
317 }
318 interface = charon->kernel_interface->get_interface(
319 charon->kernel_interface, other);
320 if (interface)
321 {
322 DBG2(DBG_CFG, "left is other host, swapping ends");
323 tmp = me;
324 me = other;
325 other = tmp;
326 tmp_end = msg->add_conn.me;
327 msg->add_conn.me = msg->add_conn.other;
328 msg->add_conn.other = tmp_end;
329 free(interface);
330 }
331 else
332 {
333 interface = charon->kernel_interface->get_interface(
334 charon->kernel_interface, me);
335 if (!interface)
336 {
337 DBG1(DBG_CFG, "left nor right host is our side, assuming left=local");
338 }
339 else
340 {
341 free(interface);
342 }
343 }
344 ike_cfg = ike_cfg_create(msg->add_conn.other.sendcert != CERT_NEVER_SEND,
345 msg->add_conn.force_encap, me, other);
346 add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL);
347 return ike_cfg;
348 }
349 /**
350 * build a peer_cfg from a stroke msg
351 */
352 static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
353 stroke_msg_t *msg, ike_cfg_t *ike_cfg)
354 {
355 identification_t *me, *other, *peer_id = NULL;
356 peer_cfg_t *mediated_by = NULL;
357 host_t *my_vip = NULL, *other_vip = NULL;
358 certificate_t *cert;
359 u_int32_t rekey = 0, reauth = 0, over, jitter;
360
361 me = identification_create_from_string(msg->add_conn.me.id ?
362 msg->add_conn.me.id : msg->add_conn.me.address);
363 if (!me)
364 {
365 DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.me.id);
366 return NULL;
367 }
368 other = identification_create_from_string(msg->add_conn.other.id ?
369 msg->add_conn.other.id : msg->add_conn.other.address);
370 if (!other)
371 {
372 DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.other.id);
373 me->destroy(me);
374 return NULL;
375 }
376
377
378 #ifdef P2P
379 if (msg->add_conn.p2p.mediation && msg->add_conn.p2p.mediated_by)
380 {
381 DBG1(DBG_CFG, "a mediation connection cannot be a"
382 " mediated connection at the same time, aborting");
383 me->destroy(me);
384 other->destroy(other);
385 return NULL;
386 }
387
388 if (msg->add_conn.p2p.mediated_by)
389 {
390 mediated_by = charon->backends->get_peer_cfg_by_name(charon->backends,
391 msg->add_conn.p2p.mediated_by);
392 if (!mediated_by)
393 {
394 DBG1(DBG_CFG, "mediation connection '%s' not found, aborting",
395 msg->add_conn.p2p.mediated_by);
396 me->destroy(me);
397 other->destroy(other);
398 return NULL;
399 }
400
401 if (!mediated_by->is_mediation(mediated_by))
402 {
403 DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is"
404 "no mediation connection, aborting",
405 msg->add_conn.p2p.mediated_by, msg->add_conn.name);
406 mediated_by->destroy(mediated_by);
407 me->destroy(me);
408 other->destroy(other);
409 return NULL;
410 }
411 }
412
413 if (msg->add_conn.p2p.peerid)
414 {
415 peer_id = identification_create_from_string(msg->add_conn.p2p.peerid);
416 if (!peer_id)
417 {
418 DBG1(DBG_CFG, "invalid peer ID: %s\n", msg->add_conn.p2p.peerid);
419 mediated_by->destroy(mediated_by);
420 me->destroy(me);
421 other->destroy(other);
422 return NULL;
423 }
424 }
425 else
426 {
427 /* no peer ID supplied, assume right ID */
428 peer_id = other_id->clone(other_id);
429 }
430 #endif /* P2P */
431
432 if (msg->add_conn.me.cert)
433 {
434 cert = this->cred->load_peer(this->cred, msg->add_conn.me.cert);
435 if (cert)
436 {
437 me = update_peerid(cert, me);
438 cert->destroy(cert);
439 }
440 }
441 if (msg->add_conn.other.cert)
442 {
443 cert = this->cred->load_peer(this->cred, msg->add_conn.other.cert);
444 if (cert)
445 {
446 other = update_peerid(cert, other);
447 cert->destroy(cert);
448 }
449 }
450 jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100;
451 over = msg->add_conn.rekey.margin;
452 if (msg->add_conn.rekey.reauth)
453 {
454 reauth = msg->add_conn.rekey.ike_lifetime - over;
455 }
456 else
457 {
458 rekey = msg->add_conn.rekey.ike_lifetime - over;
459 }
460 if (msg->add_conn.me.virtual_ip && msg->add_conn.me.sourceip)
461 {
462 my_vip = host_create_from_string(msg->add_conn.me.sourceip, 0);
463 }
464 if (msg->add_conn.other.virtual_ip && msg->add_conn.other.sourceip)
465 {
466 other_vip = host_create_from_string(msg->add_conn.other.sourceip, 0);
467 }
468 return peer_cfg_create(msg->add_conn.name,
469 msg->add_conn.ikev2 ? 2 : 1, ike_cfg, me, other,
470 msg->add_conn.me.sendcert, msg->add_conn.auth_method,
471 msg->add_conn.eap_type, msg->add_conn.eap_vendor,
472 msg->add_conn.rekey.tries, rekey, reauth, jitter, over,
473 msg->add_conn.mobike, msg->add_conn.dpd.delay, msg->add_conn.dpd.action,
474 my_vip, other_vip, msg->add_conn.p2p.mediation, mediated_by, peer_id);
475 }
476
477 /**
478 * fill in auth_info from stroke message
479 */
480 static void build_auth_info(private_stroke_config_t *this,
481 stroke_msg_t *msg, auth_info_t *auth)
482 {
483 identification_t *my_ca = NULL, *other_ca = NULL;
484 bool my_ca_same = FALSE;
485 bool other_ca_same = FALSE;
486 cert_validation_t valid;
487
488 if (msg->add_conn.other.groups)
489 {
490 /* TODO: AC groups */
491 }
492
493 switch (msg->add_conn.crl_policy)
494 {
495 case CRL_STRICT_YES:
496 valid = VALIDATION_GOOD;
497 auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
498 break;
499 case CRL_STRICT_IFURI:
500 valid = VALIDATION_SKIPPED;
501 auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
502 break;
503 default:
504 break;
505 }
506
507 if (msg->add_conn.me.ca)
508 {
509 if (streq(msg->add_conn.me.ca, "%same"))
510 {
511 my_ca_same = TRUE;
512 }
513 else
514 {
515 my_ca = identification_create_from_string(msg->add_conn.me.ca);
516 }
517 }
518 if (msg->add_conn.other.ca)
519 {
520 if (streq(msg->add_conn.other.ca, "%same"))
521 {
522 other_ca_same = TRUE;
523 }
524 else
525 {
526 other_ca = identification_create_from_string(msg->add_conn.other.ca);
527 }
528 }
529 if (other_ca_same && my_ca)
530 {
531 other_ca = my_ca->clone(my_ca);
532 }
533 else if (my_ca_same && other_ca)
534 {
535 my_ca = other_ca->clone(other_ca);
536 }
537
538 if (other_ca)
539 {
540 DBG2(DBG_CFG, " other ca: %D", other_ca);
541 certificate_t *cert = charon->credentials->get_cert(charon->credentials,
542 CERT_X509, KEY_ANY, other_ca, TRUE);
543 if (cert)
544 {
545 auth->add_item(auth, AUTHZ_CA_CERT, cert);
546 cert->destroy(cert);
547 }
548 else
549 {
550 auth->add_item(auth, AUTHZ_CA_CERT_NAME, other_ca);
551 }
552 other_ca->destroy(other_ca);
553 }
554 if (my_ca)
555 {
556 DBG2(DBG_CFG, " my ca: %D", my_ca);
557 certificate_t *cert = charon->credentials->get_cert(charon->credentials,
558 CERT_X509, KEY_ANY, my_ca, TRUE);
559 if (cert)
560 {
561 auth->add_item(auth, AUTHN_CA_CERT, cert);
562 cert->destroy(cert);
563 }
564 else
565 {
566 auth->add_item(auth, AUTHN_CA_CERT_NAME, my_ca);
567 }
568 my_ca->destroy(my_ca);
569 }
570 }
571
572 /**
573 * build a traffic selector from a stroke_end
574 */
575 static traffic_selector_t *build_ts(private_stroke_config_t *this,
576 stroke_end_t *end)
577 {
578 if (end->tohost)
579 {
580 return traffic_selector_create_dynamic(end->protocol,
581 end->port ? end->port : 0, end->port ? end->port : 65535);
582 }
583 else
584 {
585 host_t *net;
586
587 net = host_create_from_string(end->subnet ? end->subnet : end->address,
588 IKEV2_UDP_PORT);
589 if (!net)
590 {
591 DBG1(DBG_CFG, "invalid subnet: %s", end->subnet);
592 return NULL;
593 }
594 return traffic_selector_create_from_subnet(net,
595 end->subnet ? end->subnet_mask : 0, end->protocol, end->port);
596 }
597 }
598
599 /**
600 * build a child config from the stroke message
601 */
602 static child_cfg_t *build_child_cfg(private_stroke_config_t *this,
603 stroke_msg_t *msg)
604 {
605 child_cfg_t *child_cfg;
606 traffic_selector_t *ts;
607
608 child_cfg = child_cfg_create(
609 msg->add_conn.name, msg->add_conn.rekey.ipsec_lifetime,
610 msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
611 msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100,
612 msg->add_conn.me.updown, msg->add_conn.me.hostaccess,
613 msg->add_conn.mode);
614
615 ts = build_ts(this, &msg->add_conn.me);
616 if (!ts)
617 {
618 child_cfg->destroy(child_cfg);
619 return NULL;
620 }
621 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
622
623 ts = build_ts(this, &msg->add_conn.other);
624 if (!ts)
625 {
626 child_cfg->destroy(child_cfg);
627 return NULL;
628 }
629 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
630
631 add_proposals(this, msg->add_conn.algorithms.esp, NULL, child_cfg);
632
633 return child_cfg;
634 }
635
636 /**
637 * Implementation of stroke_config_t.add.
638 */
639 static void add(private_stroke_config_t *this, stroke_msg_t *msg)
640 {
641 ike_cfg_t *ike_cfg, *existing_ike;
642 peer_cfg_t *peer_cfg, *existing;
643 child_cfg_t *child_cfg;
644 enumerator_t *enumerator;
645 bool use_existing = FALSE;
646
647 ike_cfg = build_ike_cfg(this, msg);
648 if (!ike_cfg)
649 {
650 return;
651 }
652 peer_cfg = build_peer_cfg(this, msg, ike_cfg);
653 if (!peer_cfg)
654 {
655 ike_cfg->destroy(ike_cfg);
656 return;
657 }
658
659 build_auth_info(this, msg, peer_cfg->get_auth(peer_cfg));
660 enumerator = create_peer_cfg_enumerator(this, NULL, NULL);
661 while (enumerator->enumerate(enumerator, &existing))
662 {
663 existing_ike = existing->get_ike_cfg(existing);
664 if (existing->equals(existing, peer_cfg) &&
665 existing_ike->equals(existing_ike, peer_cfg->get_ike_cfg(peer_cfg)))
666 {
667 use_existing = TRUE;
668 peer_cfg->destroy(peer_cfg);
669 peer_cfg = existing;
670 peer_cfg->get_ref(peer_cfg);
671 DBG1(DBG_CFG, "added child to existing configuration '%s'",
672 peer_cfg->get_name(peer_cfg));
673 break;
674 }
675 }
676 enumerator->destroy(enumerator);
677
678 child_cfg = build_child_cfg(this, msg);
679 if (!child_cfg)
680 {
681 peer_cfg->destroy(peer_cfg);
682 return;
683 }
684 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
685
686 if (use_existing)
687 {
688 peer_cfg->destroy(peer_cfg);
689 }
690 else
691 {
692 /* add config to backend */
693 DBG1(DBG_CFG, "added configuration '%s': %H[%D]...%H[%D]", msg->add_conn.name,
694 ike_cfg->get_my_host(ike_cfg), peer_cfg->get_my_id(peer_cfg),
695 ike_cfg->get_other_host(ike_cfg), peer_cfg->get_other_id(peer_cfg));
696 this->mutex->lock(this->mutex);
697 this->list->insert_last(this->list, peer_cfg);
698 this->mutex->unlock(this->mutex);
699 }
700 }
701
702 /**
703 * Implementation of stroke_config_t.del.
704 */
705 static void del(private_stroke_config_t *this, stroke_msg_t *msg)
706 {
707 enumerator_t *enumerator, *children;
708 peer_cfg_t *peer;
709 child_cfg_t *child;
710
711 this->mutex->lock(this->mutex);
712 enumerator = this->list->create_enumerator(this->list);
713 while (enumerator->enumerate(enumerator, (void**)&peer))
714 {
715 /* remove peer config with such a name */
716 if (streq(peer->get_name(peer), msg->del_conn.name))
717 {
718 this->list->remove_at(this->list, enumerator);
719 peer->destroy(peer);
720 continue;
721 }
722 /* remove any child with such a name */
723 children = peer->create_child_cfg_enumerator(peer);
724 while (children->enumerate(children, &child))
725 {
726 if (streq(child->get_name(child), msg->del_conn.name))
727 {
728 peer->remove_child_cfg(peer, enumerator);
729 child->destroy(child);
730 }
731 }
732 children->destroy(children);
733 }
734 enumerator->destroy(enumerator);
735 this->mutex->unlock(this->mutex);
736
737 DBG1(DBG_CFG, "deleted connection '%s'", msg->del_conn.name);
738 }
739
740 /**
741 * Implementation of stroke_config_t.destroy
742 */
743 static void destroy(private_stroke_config_t *this)
744 {
745 this->list->destroy_offset(this->list, offsetof(peer_cfg_t, destroy));
746 this->mutex->destroy(this->mutex);
747 free(this);
748 }
749
750 /*
751 * see header file
752 */
753 stroke_config_t *stroke_config_create(stroke_cred_t *cred)
754 {
755 private_stroke_config_t *this = malloc_thing(private_stroke_config_t);
756
757 this->public.backend.create_peer_cfg_enumerator = (enumerator_t*(*)(backend_t*, identification_t *me, identification_t *other))create_peer_cfg_enumerator;
758 this->public.backend.create_ike_cfg_enumerator = (enumerator_t*(*)(backend_t*, host_t *me, host_t *other))create_ike_cfg_enumerator;
759 this->public.backend.get_peer_cfg_by_name = (peer_cfg_t* (*)(backend_t*,char*))get_peer_cfg_by_name;
760 this->public.add = (void(*)(stroke_config_t*, stroke_msg_t *msg))add;
761 this->public.del = (void(*)(stroke_config_t*, stroke_msg_t *msg))del;
762 this->public.destroy = (void(*)(stroke_config_t*))destroy;
763
764 this->list = linked_list_create();
765 this->mutex = mutex_create(MUTEX_RECURSIVE);
766 this->cred = cred;
767
768 return &this->public;
769 }
770