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