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