proposal-substructure: Fix incorrect type for IKEv2 proposals
[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
276 ike_cfg = ike_cfg_create(IKEV2, certreq, force_encap, local,
277 charon->socket->get_port(charon->socket, FALSE),
278 remote, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0);
279 add_ike_proposals(this, ike_cfg, id);
280 return ike_cfg;
281 }
282 return NULL;
283 }
284
285 /**
286 * Query an IKE config by its id
287 */
288 static ike_cfg_t* get_ike_cfg_by_id(private_sql_config_t *this, int id)
289 {
290 enumerator_t *e;
291 ike_cfg_t *ike_cfg = NULL;
292
293 e = this->db->query(this->db,
294 "SELECT c.id, c.certreq, c.force_encap, c.local, c.remote "
295 "FROM ike_configs AS c WHERE c.id = ?",
296 DB_INT, id,
297 DB_INT, DB_INT, DB_INT, DB_TEXT, DB_TEXT);
298 if (e)
299 {
300 ike_cfg = build_ike_cfg(this, e, NULL, NULL);
301 e->destroy(e);
302 }
303 return ike_cfg;
304 }
305
306 #ifdef ME
307 /**
308 * Query a peer config by its id
309 */
310 static peer_cfg_t *get_peer_cfg_by_id(private_sql_config_t *this, int id)
311 {
312 enumerator_t *e;
313 peer_cfg_t *peer_cfg = NULL;
314
315 e = this->db->query(this->db,
316 "SELECT c.id, c.name, c.ike_cfg, l.type, l.data, r.type, r.data, "
317 "c.cert_policy, c.uniqueid, c.auth_method, c.eap_type, "
318 "c.eap_vendor, c.keyingtries, c.rekeytime, c.reauthtime, c.jitter, "
319 "c.overtime, c.mobike, c.dpd_delay, c.virtual, c.pool, "
320 "c.mediation, c.mediated_by, COALESCE(p.type, 0), p.data "
321 "FROM peer_configs AS c "
322 "JOIN identities AS l ON c.local_id = l.id "
323 "JOIN identities AS r ON c.remote_id = r.id "
324 "LEFT JOIN identities AS p ON c.peer_id = p.id "
325 "WHERE c.id = ?",
326 DB_INT, id,
327 DB_INT, DB_TEXT, DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB,
328 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
329 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
330 DB_INT, DB_TEXT, DB_TEXT,
331 DB_INT, DB_INT, DB_INT, DB_BLOB);
332 if (e)
333 {
334 peer_cfg = build_peer_cfg(this, e, NULL, NULL);
335 e->destroy(e);
336 }
337 return peer_cfg;
338 }
339 #endif /* ME */
340
341 /**
342 * Check if the two IDs match (the first one is optional)
343 */
344 static inline bool id_matches(identification_t *id, identification_t *sql_id)
345 {
346 return !id || id->matches(id, sql_id) || sql_id->matches(sql_id, id);
347 }
348
349 /**
350 * Build a peer config from an SQL query
351 */
352 static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e,
353 identification_t *me, identification_t *other)
354 {
355 int id, ike_cfg, l_type, r_type,
356 cert_policy, uniqueid, auth_method, eap_type, eap_vendor, keyingtries,
357 rekeytime, reauthtime, jitter, overtime, mobike, dpd_delay,
358 mediation, mediated_by, p_type;
359 chunk_t l_data, r_data, p_data;
360 char *name, *virtual, *pool;
361 enumerator_t *enumerator;
362
363 while (e->enumerate(e,
364 &id, &name, &ike_cfg, &l_type, &l_data, &r_type, &r_data,
365 &cert_policy, &uniqueid, &auth_method, &eap_type, &eap_vendor,
366 &keyingtries, &rekeytime, &reauthtime, &jitter, &overtime, &mobike,
367 &dpd_delay, &virtual, &pool,
368 &mediation, &mediated_by, &p_type, &p_data))
369 {
370 identification_t *local_id, *remote_id, *peer_id = NULL;
371 peer_cfg_t *peer_cfg, *mediated_cfg = NULL;
372 ike_cfg_t *ike;
373 host_t *vip = NULL;
374 auth_cfg_t *auth;
375
376 local_id = identification_create_from_encoding(l_type, l_data);
377 remote_id = identification_create_from_encoding(r_type, r_data);
378 if (!id_matches(me, local_id) || !id_matches(other, remote_id))
379 {
380 local_id->destroy(local_id);
381 remote_id->destroy(remote_id);
382 continue;
383 }
384 ike = get_ike_cfg_by_id(this, ike_cfg);
385
386 #ifdef ME
387 mediated_cfg = mediated_by ? get_peer_cfg_by_id(this, mediated_by)
388 : NULL;
389 if (p_type)
390 {
391 peer_id = identification_create_from_encoding(p_type, p_data);
392 }
393 #endif /* ME */
394
395 if (virtual)
396 {
397 vip = host_create_from_string(virtual, 0);
398 }
399 if (ike)
400 {
401 peer_cfg_create_t peer = {
402 .cert_policy = cert_policy,
403 .unique = uniqueid,
404 .keyingtries = keyingtries,
405 .rekey_time = rekeytime,
406 .reauth_time = reauthtime,
407 .jitter_time = jitter,
408 .over_time = overtime,
409 .no_mobike = !mobike,
410 .dpd = dpd_delay,
411 #ifdef ME
412 .mediation = mediation,
413 .mediated_by = mediated_cfg ?
414 mediated_cfg->get_name(mediated_cfg) : NULL,
415 .peer_id = peer_id,
416 #endif /* ME */
417 };
418
419 peer_cfg = peer_cfg_create(name, ike, &peer);
420 if (vip)
421 {
422 peer_cfg->add_virtual_ip(peer_cfg, vip);
423 }
424 if (pool)
425 {
426 /* attr-sql used comma separated pools, but we now completely
427 * support multiple pools directly. Support old SQL configs: */
428 enumerator = enumerator_create_token(pool, ",", " ");
429 while (enumerator->enumerate(enumerator, &pool))
430 {
431 peer_cfg->add_pool(peer_cfg, pool);
432 }
433 enumerator->destroy(enumerator);
434 }
435 auth = auth_cfg_create();
436 auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_method);
437 auth->add(auth, AUTH_RULE_IDENTITY, local_id);
438 peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
439 auth = auth_cfg_create();
440 auth->add(auth, AUTH_RULE_IDENTITY, remote_id);
441 if (eap_type)
442 {
443 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
444 auth->add(auth, AUTH_RULE_EAP_TYPE, eap_type);
445 if (eap_vendor)
446 {
447 auth->add(auth, AUTH_RULE_EAP_VENDOR, eap_vendor);
448 }
449 }
450 peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
451 add_child_cfgs(this, peer_cfg, id);
452 DESTROY_IF(mediated_cfg);
453 return peer_cfg;
454 }
455 DESTROY_IF(ike);
456 DESTROY_IF(mediated_cfg);
457 DESTROY_IF(peer_id);
458 DESTROY_IF(local_id);
459 DESTROY_IF(remote_id);
460 }
461 return NULL;
462 }
463
464 METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*,
465 private_sql_config_t *this, char *name)
466 {
467 enumerator_t *e;
468 peer_cfg_t *peer_cfg = NULL;
469
470 e = this->db->query(this->db,
471 "SELECT c.id, c.name, c.ike_cfg, l.type, l.data, r.type, r.data, "
472 "c.cert_policy, c.uniqueid, c.auth_method, c.eap_type, "
473 "c.eap_vendor, c.keyingtries, c.rekeytime, c.reauthtime, c.jitter, "
474 "c.overtime, c.mobike, c.dpd_delay, c.virtual, c.pool, "
475 "c.mediation, c.mediated_by, COALESCE(p.type, 0), p.data "
476 "FROM peer_configs AS c "
477 "JOIN identities AS l ON c.local_id = l.id "
478 "JOIN identities AS r ON c.remote_id = r.id "
479 "LEFT JOIN identities AS p ON c.peer_id = p.id "
480 "WHERE c.ike_version = ? AND c.name = ?",
481 DB_INT, 2, DB_TEXT, name,
482 DB_INT, DB_TEXT, DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB,
483 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
484 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
485 DB_INT, DB_TEXT, DB_TEXT,
486 DB_INT, DB_INT, DB_INT, DB_BLOB);
487 if (e)
488 {
489 peer_cfg = build_peer_cfg(this, e, NULL, NULL);
490 e->destroy(e);
491 }
492 return peer_cfg;
493 }
494
495 typedef struct {
496 /** implements enumerator */
497 enumerator_t public;
498 /** reference to context */
499 private_sql_config_t *this;
500 /** filtering own host */
501 host_t *me;
502 /** filtering remote host */
503 host_t *other;
504 /** inner SQL enumerator */
505 enumerator_t *inner;
506 /** currently enumerated peer config */
507 ike_cfg_t *current;
508 } ike_enumerator_t;
509
510 METHOD(enumerator_t, ike_enumerator_enumerate, bool,
511 ike_enumerator_t *this, va_list args)
512 {
513 ike_cfg_t **cfg;
514
515 VA_ARGS_VGET(args, cfg);
516 DESTROY_IF(this->current);
517 this->current = build_ike_cfg(this->this, this->inner, this->me, this->other);
518 if (this->current)
519 {
520 *cfg = this->current;
521 return TRUE;
522 }
523 return FALSE;
524 }
525
526 METHOD(enumerator_t, ike_enumerator_destroy, void,
527 ike_enumerator_t *this)
528 {
529 DESTROY_IF(this->current);
530 this->inner->destroy(this->inner);
531 free(this);
532 }
533
534 METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*,
535 private_sql_config_t *this, host_t *me, host_t *other)
536 {
537 ike_enumerator_t *e;
538
539 INIT(e,
540 .public = {
541 .enumerate = enumerator_enumerate_default,
542 .venumerate = _ike_enumerator_enumerate,
543 .destroy = _ike_enumerator_destroy,
544 },
545 .this = this,
546 .me = me,
547 .other = other,
548 );
549 e->inner = this->db->query(this->db,
550 "SELECT c.id, c.certreq, c.force_encap, "
551 "c.local, c.remote FROM ike_configs AS c",
552 DB_INT, DB_INT, DB_INT, DB_TEXT, DB_TEXT);
553 if (!e->inner)
554 {
555 free(e);
556 return NULL;
557 }
558 return &e->public;
559 }
560
561
562 typedef struct {
563 /** implements enumerator */
564 enumerator_t public;
565 /** reference to context */
566 private_sql_config_t *this;
567 /** filtering own identity */
568 identification_t *me;
569 /** filtering remote identity */
570 identification_t *other;
571 /** inner SQL enumerator */
572 enumerator_t *inner;
573 /** currently enumerated peer config */
574 peer_cfg_t *current;
575 } peer_enumerator_t;
576
577 METHOD(enumerator_t, peer_enumerator_enumerate, bool,
578 peer_enumerator_t *this, va_list args)
579 {
580 peer_cfg_t **cfg;
581
582 VA_ARGS_VGET(args, cfg);
583 DESTROY_IF(this->current);
584 this->current = build_peer_cfg(this->this, this->inner, this->me, this->other);
585 if (this->current)
586 {
587 *cfg = this->current;
588 return TRUE;
589 }
590 return FALSE;
591 }
592
593 METHOD(enumerator_t, peer_enumerator_destroy, void,
594 peer_enumerator_t *this)
595 {
596 DESTROY_IF(this->current);
597 this->inner->destroy(this->inner);
598 free(this);
599 }
600
601 METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
602 private_sql_config_t *this, identification_t *me, identification_t *other)
603 {
604 peer_enumerator_t *e;
605
606 INIT(e,
607 .public = {
608 .enumerate = enumerator_enumerate_default,
609 .venumerate = _peer_enumerator_enumerate,
610 .destroy = _peer_enumerator_destroy,
611 },
612 .this = this,
613 .me = me,
614 .other = other,
615 );
616
617 /* TODO: only get configs whose IDs match exactly or contain wildcards */
618 e->inner = this->db->query(this->db,
619 "SELECT c.id, c.name, c.ike_cfg, l.type, l.data, r.type, r.data, "
620 "c.cert_policy, c.uniqueid, c.auth_method, c.eap_type, "
621 "c.eap_vendor, c.keyingtries, c.rekeytime, c.reauthtime, c.jitter, "
622 "c.overtime, c.mobike, c.dpd_delay, c.virtual, c.pool, "
623 "c.mediation, c.mediated_by, COALESCE(p.type, 0), p.data "
624 "FROM peer_configs AS c "
625 "JOIN identities AS l ON c.local_id = l.id "
626 "JOIN identities AS r ON c.remote_id = r.id "
627 "LEFT JOIN identities AS p ON c.peer_id = p.id "
628 "WHERE c.ike_version = ?",
629 DB_INT, 2,
630 DB_INT, DB_TEXT, DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB,
631 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
632 DB_INT, DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
633 DB_INT, DB_TEXT, DB_TEXT,
634 DB_INT, DB_INT, DB_INT, DB_BLOB);
635 if (!e->inner)
636 {
637 free(e);
638 return NULL;
639 }
640 return &e->public;
641 }
642
643 METHOD(sql_config_t, destroy, void,
644 private_sql_config_t *this)
645 {
646 free(this);
647 }
648
649 /**
650 * Described in header.
651 */
652 sql_config_t *sql_config_create(database_t *db)
653 {
654 private_sql_config_t *this;
655
656 INIT(this,
657 .public = {
658 .backend = {
659 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
660 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
661 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
662 },
663 .destroy = _destroy,
664 },
665 .db = db
666 );
667
668 return &this->public;
669 }