]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libcharon/plugins/sql/sql_config.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libcharon / plugins / sql / sql_config.c
1 /*
2 * Copyright (C) 2006-2008 Martin Willi
3 * Copyright (C) 2010 Andreas Steffen
4 *
5 * Copyright (C) secunet Security Networks AG
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include <string.h>
19
20 #include "sql_config.h"
21
22 #include <daemon.h>
23
24 typedef struct private_sql_config_t private_sql_config_t;
25
26 /**
27 * Private data of an sql_config_t object
28 */
29 struct private_sql_config_t {
30
31 /**
32 * Public part
33 */
34 sql_config_t public;
35
36 /**
37 * database connection
38 */
39 database_t *db;
40 };
41
42 /**
43 * Forward declaration
44 */
45 static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e,
46 identification_t *me, identification_t *other);
47
48 /**
49 * Build a traffic selector from an SQL query
50 */
51 static traffic_selector_t *build_traffic_selector(private_sql_config_t *this,
52 enumerator_t *e, bool *local)
53 {
54 int type, protocol, start_port, end_port;
55 chunk_t start_addr, end_addr;
56 traffic_selector_t *ts;
57 enum {
58 TS_LOCAL = 0,
59 TS_REMOTE = 1,
60 TS_LOCAL_DYNAMIC = 2,
61 TS_REMOTE_DYNAMIC = 3,
62 } kind;
63
64 while (e->enumerate(e, &kind, &type, &protocol,
65 &start_addr, &end_addr, &start_port, &end_port))
66 {
67 *local = FALSE;
68 switch (kind)
69 {
70 case TS_LOCAL:
71 *local = TRUE;
72 /* FALL */
73 case TS_REMOTE:
74 ts = traffic_selector_create_from_bytes(protocol, type,
75 start_addr, start_port, end_addr, end_port);
76 break;
77 case TS_LOCAL_DYNAMIC:
78 *local = TRUE;
79 /* FALL */
80 case TS_REMOTE_DYNAMIC:
81 ts = traffic_selector_create_dynamic(protocol,
82 start_port, end_port);
83 break;
84 default:
85 continue;
86 }
87 if (ts)
88 {
89 return ts;
90 }
91 }
92 return NULL;
93 }
94
95 /**
96 * Add traffic selectors to a child config
97 */
98 static void add_traffic_selectors(private_sql_config_t *this,
99 child_cfg_t *child, int id)
100 {
101 enumerator_t *e;
102 traffic_selector_t *ts;
103 bool local;
104
105 e = this->db->query(this->db,
106 "SELECT ct.kind, t.type, t.protocol, "
107 "t.start_addr, t.end_addr, t.start_port, t.end_port "
108 "FROM traffic_selectors AS t "
109 "JOIN child_config_traffic_selector AS ct "
110 "ON t.id = ct.traffic_selector WHERE ct.child_cfg = ?",
111 DB_INT, id,
112 DB_INT, DB_INT, DB_INT,
113 DB_BLOB, DB_BLOB, DB_INT, DB_INT);
114 if (e)
115 {
116 while ((ts = build_traffic_selector(this, e, &local)))
117 {
118 child->add_traffic_selector(child, local, ts);
119 }
120 e->destroy(e);
121 }
122 }
123
124 /**
125 * Add ESP proposals to a child config
126 */
127 static void add_esp_proposals(private_sql_config_t *this,
128 child_cfg_t *child, int id)
129 {
130 enumerator_t *e;
131 proposal_t *proposal;
132 char *prop;
133 bool use_default = TRUE;
134
135 e = this->db->query(this->db,
136 "SELECT p.proposal "
137 "FROM proposals AS p JOIN child_config_proposal AS cp "
138 "ON p.id = cp.prop WHERE cp.child_cfg = ? ORDER BY cp.prio",
139 DB_INT, id, DB_TEXT);
140 if (e)
141 {
142 while (e->enumerate(e, &prop))
143 {
144 proposal = proposal_create_from_string(PROTO_ESP, prop);
145 if (!proposal)
146 {
147 DBG1(DBG_CFG, "could not create ESP proposal from '%s'", prop);
148 break;
149 }
150 child->add_proposal(child, proposal);
151 use_default = FALSE;
152 }
153 e->destroy(e);
154 }
155 if (use_default)
156 {
157 child->add_proposal(child, proposal_create_default_aead(PROTO_ESP));
158 child->add_proposal(child, proposal_create_default(PROTO_ESP));
159 }
160 }
161
162 /**
163 * Build a child config from an SQL query
164 */
165 static child_cfg_t *build_child_cfg(private_sql_config_t *this, enumerator_t *e)
166 {
167 int id, lifetime, rekeytime, jitter, hostaccess, mode, ipcomp, reqid;
168 int start, dpd, close;
169 char *name, *updown;
170 child_cfg_t *child_cfg;
171
172 if (e->enumerate(e, &id, &name, &lifetime, &rekeytime, &jitter, &updown,
173 &hostaccess, &mode, &start, &dpd, &close, &ipcomp, &reqid))
174 {
175 child_cfg_create_t child = {
176 .mode = mode,
177 .reqid = reqid,
178 .options = (ipcomp ? OPT_IPCOMP : 0) |
179 (hostaccess ? OPT_HOSTACCESS : 0),
180 .lifetime = {
181 .time = {
182 .life = lifetime, .rekey = rekeytime, .jitter = jitter
183 },
184 },
185 .start_action = start,
186 .dpd_action = dpd,
187 .close_action = close,
188 .updown = updown,
189 };
190 child_cfg = child_cfg_create(name, &child);
191 add_esp_proposals(this, child_cfg, id);
192 add_traffic_selectors(this, child_cfg, id);
193 return child_cfg;
194 }
195 return NULL;
196 }
197
198 /**
199 * Add child configs to peer config
200 */
201 static void add_child_cfgs(private_sql_config_t *this, peer_cfg_t *peer, int id)
202 {
203 enumerator_t *e;
204 child_cfg_t *child_cfg;
205
206 e = this->db->query(this->db,
207 "SELECT c.id, c.name, c.lifetime, c.rekeytime, c.jitter, c.updown, "
208 "c.hostaccess, c.mode, c.start_action, c.dpd_action, "
209 "c.close_action, c.ipcomp, c.reqid "
210 "FROM child_configs AS c JOIN peer_config_child_config AS pc "
211 "ON c.id = pc.child_cfg WHERE pc.peer_cfg = ?",
212 DB_INT, id,
213 DB_INT, DB_TEXT, DB_INT, DB_INT, DB_INT, DB_TEXT, DB_INT,
214 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT, DB_INT);
215 if (e)
216 {
217 while ((child_cfg = build_child_cfg(this, e)))
218 {
219 peer->add_child_cfg(peer, child_cfg);
220 }
221 e->destroy(e);
222 }
223 }
224
225 /**
226 * Add IKE proposals to an IKE config
227 */
228 static void add_ike_proposals(private_sql_config_t *this,
229 ike_cfg_t *ike_cfg, int id)
230 {
231 enumerator_t *e;
232 proposal_t *proposal;
233 char *prop;
234 bool use_default = TRUE;
235
236 e = this->db->query(this->db,
237 "SELECT p.proposal "
238 "FROM proposals AS p "
239 "JOIN ike_config_proposal AS ip ON p.id = ip.prop "
240 "WHERE ip.ike_cfg = ? ORDER BY ip.prio",
241 DB_INT, id, DB_TEXT);
242 if (e)
243 {
244 while (e->enumerate(e, &prop))
245 {
246 proposal = proposal_create_from_string(PROTO_IKE, prop);
247 if (!proposal)
248 {
249 DBG1(DBG_CFG, "could not create IKE proposal from '%s'", prop);
250 break;
251 }
252 ike_cfg->add_proposal(ike_cfg, proposal);
253 use_default = FALSE;
254 }
255 e->destroy(e);
256 }
257 if (use_default)
258 {
259 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
260 ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
261 }
262 }
263
264 /**
265 * Build an IKE config from an SQL query
266 */
267 static ike_cfg_t *build_ike_cfg(private_sql_config_t *this, enumerator_t *e,
268 host_t *my_host, host_t *other_host)
269 {
270 int id, certreq, force_encap;
271 char *local, *remote;
272
273 while (e->enumerate(e, &id, &certreq, &force_encap, &local, &remote))
274 {
275 ike_cfg_t *ike_cfg;
276 ike_cfg_create_t ike = {
277 .version = IKEV2,
278 .local = local,
279 .local_port = charon->socket->get_port(charon->socket, FALSE),
280 .remote = remote,
281 .remote_port = IKEV2_UDP_PORT,
282 .no_certreq = !certreq,
283 .force_encap = force_encap,
284 .fragmentation = FRAGMENTATION_YES,
285 };
286
287 ike_cfg = ike_cfg_create(&ike);
288 add_ike_proposals(this, ike_cfg, id);
289 return ike_cfg;
290 }
291 return NULL;
292 }
293
294 /**
295 * Query an IKE config by its id
296 */
297 static ike_cfg_t* get_ike_cfg_by_id(private_sql_config_t *this, int id)
298 {
299 enumerator_t *e;
300 ike_cfg_t *ike_cfg = NULL;
301
302 e = this->db->query(this->db,
303 "SELECT c.id, c.certreq, c.force_encap, c.local, c.remote "
304 "FROM ike_configs AS c WHERE c.id = ?",
305 DB_INT, id,
306 DB_INT, DB_INT, DB_INT, DB_TEXT, DB_TEXT);
307 if (e)
308 {
309 ike_cfg = build_ike_cfg(this, e, NULL, NULL);
310 e->destroy(e);
311 }
312 return ike_cfg;
313 }
314
315 #ifdef ME
316 /**
317 * Query a peer config by its id
318 */
319 static peer_cfg_t *get_peer_cfg_by_id(private_sql_config_t *this, int id)
320 {
321 enumerator_t *e;
322 peer_cfg_t *peer_cfg = NULL;
323
324 e = this->db->query(this->db,
325 "SELECT c.id, c.name, c.ike_cfg, l.type, l.data, r.type, r.data, "
326 "c.cert_policy, c.uniqueid, c.auth_method, c.eap_type, "
327 "c.eap_vendor, c.keyingtries, c.rekeytime, c.reauthtime, c.jitter, "
328 "c.overtime, c.mobike, c.dpd_delay, c.virtual, c.pool, "
329 "c.mediation, c.mediated_by, COALESCE(p.type, 0), p.data "
330 "FROM peer_configs AS c "
331 "JOIN identities AS l ON c.local_id = l.id "
332 "JOIN identities AS r ON c.remote_id = r.id "
333 "LEFT JOIN identities AS p ON c.peer_id = p.id "
334 "WHERE c.id = ?",
335 DB_INT, id,
336 DB_INT, DB_TEXT, DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB,
337 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
338 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
339 DB_INT, DB_TEXT, DB_TEXT,
340 DB_INT, DB_INT, DB_INT, DB_BLOB);
341 if (e)
342 {
343 peer_cfg = build_peer_cfg(this, e, NULL, NULL);
344 e->destroy(e);
345 }
346 return peer_cfg;
347 }
348 #endif /* ME */
349
350 /**
351 * Check if the two IDs match (the first one is optional)
352 */
353 static inline bool id_matches(identification_t *id, identification_t *sql_id)
354 {
355 return !id || id->matches(id, sql_id) || sql_id->matches(sql_id, id);
356 }
357
358 /**
359 * Build a peer config from an SQL query
360 */
361 static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e,
362 identification_t *me, identification_t *other)
363 {
364 int id, ike_cfg, l_type, r_type,
365 cert_policy, uniqueid, auth_method, eap_type, eap_vendor, keyingtries,
366 rekeytime, reauthtime, jitter, overtime, mobike, dpd_delay,
367 mediation, mediated_by, p_type;
368 chunk_t l_data, r_data, p_data;
369 char *name, *virtual, *pool;
370 enumerator_t *enumerator;
371
372 while (e->enumerate(e,
373 &id, &name, &ike_cfg, &l_type, &l_data, &r_type, &r_data,
374 &cert_policy, &uniqueid, &auth_method, &eap_type, &eap_vendor,
375 &keyingtries, &rekeytime, &reauthtime, &jitter, &overtime, &mobike,
376 &dpd_delay, &virtual, &pool,
377 &mediation, &mediated_by, &p_type, &p_data))
378 {
379 identification_t *local_id, *remote_id, *peer_id = NULL;
380 peer_cfg_t *peer_cfg, *mediated_cfg = NULL;
381 ike_cfg_t *ike;
382 host_t *vip = NULL;
383 auth_cfg_t *auth;
384
385 local_id = identification_create_from_encoding(l_type, l_data);
386 remote_id = identification_create_from_encoding(r_type, r_data);
387 if (!id_matches(me, local_id) || !id_matches(other, remote_id))
388 {
389 local_id->destroy(local_id);
390 remote_id->destroy(remote_id);
391 continue;
392 }
393 ike = get_ike_cfg_by_id(this, ike_cfg);
394
395 #ifdef ME
396 mediated_cfg = mediated_by ? get_peer_cfg_by_id(this, mediated_by)
397 : NULL;
398 if (p_type)
399 {
400 peer_id = identification_create_from_encoding(p_type, p_data);
401 }
402 #endif /* ME */
403
404 if (virtual)
405 {
406 vip = host_create_from_string(virtual, 0);
407 }
408 if (ike)
409 {
410 peer_cfg_create_t peer = {
411 .cert_policy = cert_policy,
412 .unique = uniqueid,
413 .keyingtries = keyingtries,
414 .rekey_time = rekeytime,
415 .reauth_time = reauthtime,
416 .jitter_time = jitter,
417 .over_time = overtime,
418 .no_mobike = !mobike,
419 .dpd = dpd_delay,
420 #ifdef ME
421 .mediation = mediation,
422 .mediated_by = mediated_cfg ?
423 mediated_cfg->get_name(mediated_cfg) : NULL,
424 .peer_id = peer_id,
425 #endif /* ME */
426 };
427
428 peer_cfg = peer_cfg_create(name, ike, &peer);
429 if (vip)
430 {
431 peer_cfg->add_virtual_ip(peer_cfg, vip);
432 }
433 if (pool)
434 {
435 /* attr-sql used comma separated pools, but we now completely
436 * support multiple pools directly. Support old SQL configs: */
437 enumerator = enumerator_create_token(pool, ",", " ");
438 while (enumerator->enumerate(enumerator, &pool))
439 {
440 peer_cfg->add_pool(peer_cfg, pool);
441 }
442 enumerator->destroy(enumerator);
443 }
444 auth = auth_cfg_create();
445 auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_method);
446 auth->add(auth, AUTH_RULE_IDENTITY, local_id);
447 peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
448 auth = auth_cfg_create();
449 auth->add(auth, AUTH_RULE_IDENTITY, remote_id);
450 if (eap_type)
451 {
452 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
453 auth->add(auth, AUTH_RULE_EAP_TYPE, eap_type);
454 if (eap_vendor)
455 {
456 auth->add(auth, AUTH_RULE_EAP_VENDOR, eap_vendor);
457 }
458 }
459 peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
460 add_child_cfgs(this, peer_cfg, id);
461 DESTROY_IF(mediated_cfg);
462 return peer_cfg;
463 }
464 DESTROY_IF(ike);
465 DESTROY_IF(mediated_cfg);
466 DESTROY_IF(peer_id);
467 DESTROY_IF(local_id);
468 DESTROY_IF(remote_id);
469 }
470 return NULL;
471 }
472
473 METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*,
474 private_sql_config_t *this, char *name)
475 {
476 enumerator_t *e;
477 peer_cfg_t *peer_cfg = NULL;
478
479 e = this->db->query(this->db,
480 "SELECT c.id, c.name, c.ike_cfg, l.type, l.data, r.type, r.data, "
481 "c.cert_policy, c.uniqueid, c.auth_method, c.eap_type, "
482 "c.eap_vendor, c.keyingtries, c.rekeytime, c.reauthtime, c.jitter, "
483 "c.overtime, c.mobike, c.dpd_delay, c.virtual, c.pool, "
484 "c.mediation, c.mediated_by, COALESCE(p.type, 0), p.data "
485 "FROM peer_configs AS c "
486 "JOIN identities AS l ON c.local_id = l.id "
487 "JOIN identities AS r ON c.remote_id = r.id "
488 "LEFT JOIN identities AS p ON c.peer_id = p.id "
489 "WHERE c.ike_version = ? AND c.name = ?",
490 DB_INT, 2, DB_TEXT, name,
491 DB_INT, DB_TEXT, DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB,
492 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
493 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
494 DB_INT, DB_TEXT, DB_TEXT,
495 DB_INT, DB_INT, DB_INT, DB_BLOB);
496 if (e)
497 {
498 peer_cfg = build_peer_cfg(this, e, NULL, NULL);
499 e->destroy(e);
500 }
501 return peer_cfg;
502 }
503
504 typedef struct {
505 /** implements enumerator */
506 enumerator_t public;
507 /** reference to context */
508 private_sql_config_t *this;
509 /** filtering own host */
510 host_t *me;
511 /** filtering remote host */
512 host_t *other;
513 /** inner SQL enumerator */
514 enumerator_t *inner;
515 /** currently enumerated peer config */
516 ike_cfg_t *current;
517 } ike_enumerator_t;
518
519 METHOD(enumerator_t, ike_enumerator_enumerate, bool,
520 ike_enumerator_t *this, va_list args)
521 {
522 ike_cfg_t **cfg;
523
524 VA_ARGS_VGET(args, cfg);
525 DESTROY_IF(this->current);
526 this->current = build_ike_cfg(this->this, this->inner, this->me, this->other);
527 if (this->current)
528 {
529 *cfg = this->current;
530 return TRUE;
531 }
532 return FALSE;
533 }
534
535 METHOD(enumerator_t, ike_enumerator_destroy, void,
536 ike_enumerator_t *this)
537 {
538 DESTROY_IF(this->current);
539 this->inner->destroy(this->inner);
540 free(this);
541 }
542
543 METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*,
544 private_sql_config_t *this, host_t *me, host_t *other)
545 {
546 ike_enumerator_t *e;
547
548 INIT(e,
549 .public = {
550 .enumerate = enumerator_enumerate_default,
551 .venumerate = _ike_enumerator_enumerate,
552 .destroy = _ike_enumerator_destroy,
553 },
554 .this = this,
555 .me = me,
556 .other = other,
557 );
558 e->inner = this->db->query(this->db,
559 "SELECT c.id, c.certreq, c.force_encap, "
560 "c.local, c.remote FROM ike_configs AS c",
561 DB_INT, DB_INT, DB_INT, DB_TEXT, DB_TEXT);
562 if (!e->inner)
563 {
564 free(e);
565 return NULL;
566 }
567 return &e->public;
568 }
569
570
571 typedef struct {
572 /** implements enumerator */
573 enumerator_t public;
574 /** reference to context */
575 private_sql_config_t *this;
576 /** filtering own identity */
577 identification_t *me;
578 /** filtering remote identity */
579 identification_t *other;
580 /** inner SQL enumerator */
581 enumerator_t *inner;
582 /** currently enumerated peer config */
583 peer_cfg_t *current;
584 } peer_enumerator_t;
585
586 METHOD(enumerator_t, peer_enumerator_enumerate, bool,
587 peer_enumerator_t *this, va_list args)
588 {
589 peer_cfg_t **cfg;
590
591 VA_ARGS_VGET(args, cfg);
592 DESTROY_IF(this->current);
593 this->current = build_peer_cfg(this->this, this->inner, this->me, this->other);
594 if (this->current)
595 {
596 *cfg = this->current;
597 return TRUE;
598 }
599 return FALSE;
600 }
601
602 METHOD(enumerator_t, peer_enumerator_destroy, void,
603 peer_enumerator_t *this)
604 {
605 DESTROY_IF(this->current);
606 this->inner->destroy(this->inner);
607 free(this);
608 }
609
610 METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
611 private_sql_config_t *this, identification_t *me, identification_t *other)
612 {
613 peer_enumerator_t *e;
614
615 INIT(e,
616 .public = {
617 .enumerate = enumerator_enumerate_default,
618 .venumerate = _peer_enumerator_enumerate,
619 .destroy = _peer_enumerator_destroy,
620 },
621 .this = this,
622 .me = me,
623 .other = other,
624 );
625
626 /* TODO: only get configs whose IDs match exactly or contain wildcards */
627 e->inner = this->db->query(this->db,
628 "SELECT c.id, c.name, c.ike_cfg, l.type, l.data, r.type, r.data, "
629 "c.cert_policy, c.uniqueid, c.auth_method, c.eap_type, "
630 "c.eap_vendor, c.keyingtries, c.rekeytime, c.reauthtime, c.jitter, "
631 "c.overtime, c.mobike, c.dpd_delay, c.virtual, c.pool, "
632 "c.mediation, c.mediated_by, COALESCE(p.type, 0), p.data "
633 "FROM peer_configs AS c "
634 "JOIN identities AS l ON c.local_id = l.id "
635 "JOIN identities AS r ON c.remote_id = r.id "
636 "LEFT JOIN identities AS p ON c.peer_id = p.id "
637 "WHERE c.ike_version = ?",
638 DB_INT, 2,
639 DB_INT, DB_TEXT, DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB,
640 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
641 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
642 DB_INT, DB_TEXT, DB_TEXT,
643 DB_INT, DB_INT, DB_INT, DB_BLOB);
644 if (!e->inner)
645 {
646 free(e);
647 return NULL;
648 }
649 return &e->public;
650 }
651
652 METHOD(sql_config_t, destroy, void,
653 private_sql_config_t *this)
654 {
655 free(this);
656 }
657
658 /**
659 * Described in header.
660 */
661 sql_config_t *sql_config_create(database_t *db)
662 {
663 private_sql_config_t *this;
664
665 INIT(this,
666 .public = {
667 .backend = {
668 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
669 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
670 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
671 },
672 .destroy = _destroy,
673 },
674 .db = db
675 );
676
677 return &this->public;
678 }