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