789c01bae9ea5d749f6516a27199f006557bd352
[strongswan.git] / src / libcharon / plugins / medcli / medcli_config.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17 #include <string.h>
18
19 #include "medcli_config.h"
20
21 #include <daemon.h>
22 #include <processing/jobs/callback_job.h>
23
24 typedef struct private_medcli_config_t private_medcli_config_t;
25
26 /**
27 * Name of the mediation connection
28 */
29 #define MEDIATION_CONN_NAME "medcli-mediation"
30
31 /**
32 * Private data of an medcli_config_t object
33 */
34 struct private_medcli_config_t {
35
36 /**
37 * Public part
38 */
39 medcli_config_t public;
40
41 /**
42 * database connection
43 */
44 database_t *db;
45
46 /**
47 * rekey time
48 */
49 int rekey;
50
51 /**
52 * dpd delay
53 */
54 int dpd;
55
56 /**
57 * default ike config
58 */
59 ike_cfg_t *ike;
60 };
61
62 /**
63 * create a traffic selector from a CIDR notation string
64 */
65 static traffic_selector_t *ts_from_string(char *str)
66 {
67 if (str)
68 {
69 traffic_selector_t *ts;
70
71 ts = traffic_selector_create_from_cidr(str, 0, 0, 65535);
72 if (ts)
73 {
74 return ts;
75 }
76 }
77 return traffic_selector_create_dynamic(0, 0, 65535);
78 }
79
80 /**
81 * Build a mediation config
82 */
83 static peer_cfg_t *build_mediation_config(private_medcli_config_t *this,
84 peer_cfg_create_t *defaults)
85 {
86 enumerator_t *e;
87 auth_cfg_t *auth;
88 ike_cfg_t *ike_cfg;
89 peer_cfg_t *med_cfg;
90 peer_cfg_create_t peer = *defaults;
91 chunk_t me, other;
92 char *address;
93
94 /* query mediation server config:
95 * - build ike_cfg/peer_cfg for mediation connection on-the-fly
96 */
97 e = this->db->query(this->db,
98 "SELECT Address, ClientConfig.KeyId, MediationServerConfig.KeyId "
99 "FROM MediationServerConfig JOIN ClientConfig",
100 DB_TEXT, DB_BLOB, DB_BLOB);
101 if (!e || !e->enumerate(e, &address, &me, &other))
102 {
103 DESTROY_IF(e);
104 return NULL;
105 }
106 ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0",
107 charon->socket->get_port(charon->socket, FALSE),
108 address, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0);
109 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
110 ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
111
112 peer.mediation = TRUE;
113 med_cfg = peer_cfg_create(MEDIATION_CONN_NAME, ike_cfg, &peer);
114 e->destroy(e);
115
116 auth = auth_cfg_create();
117 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
118 auth->add(auth, AUTH_RULE_IDENTITY,
119 identification_create_from_encoding(ID_KEY_ID, me));
120 med_cfg->add_auth_cfg(med_cfg, auth, TRUE);
121 auth = auth_cfg_create();
122 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
123 auth->add(auth, AUTH_RULE_IDENTITY,
124 identification_create_from_encoding(ID_KEY_ID, other));
125 med_cfg->add_auth_cfg(med_cfg, auth, FALSE);
126 return med_cfg;
127 }
128
129 METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*,
130 private_medcli_config_t *this, char *name)
131 {
132 enumerator_t *e;
133 auth_cfg_t *auth;
134 peer_cfg_t *peer_cfg;
135 child_cfg_t *child_cfg;
136 chunk_t me, other;
137 char *local_net, *remote_net;
138 peer_cfg_create_t peer = {
139 .cert_policy = CERT_NEVER_SEND,
140 .unique = UNIQUE_REPLACE,
141 .keyingtries = 1,
142 .rekey_time = this->rekey * 60,
143 .jitter_time = this->rekey * 5,
144 .over_time = this->rekey * 3,
145 .dpd = this->dpd,
146 };
147 child_cfg_create_t child = {
148 .lifetime = {
149 .time = {
150 .life = this->rekey * 60 + this->rekey,
151 .rekey = this->rekey,
152 .jitter = this->rekey
153 },
154 },
155 .mode = MODE_TUNNEL,
156 };
157
158 if (streq(name, "medcli-mediation"))
159 {
160 return build_mediation_config(this, &peer);
161 }
162
163 /* query mediated config:
164 * - use any-any ike_cfg
165 * - build peer_cfg on-the-fly using med_cfg
166 * - add a child_cfg
167 */
168 e = this->db->query(this->db,
169 "SELECT ClientConfig.KeyId, Connection.KeyId, "
170 "Connection.LocalSubnet, Connection.RemoteSubnet "
171 "FROM ClientConfig JOIN Connection "
172 "WHERE Active AND Alias = ?", DB_TEXT, name,
173 DB_BLOB, DB_BLOB, DB_TEXT, DB_TEXT);
174 if (!e || !e->enumerate(e, &me, &other, &local_net, &remote_net))
175 {
176 DESTROY_IF(e);
177 return NULL;
178 }
179 peer.mediated_by = MEDIATION_CONN_NAME;
180 peer.peer_id = identification_create_from_encoding(ID_KEY_ID, other);
181 peer_cfg = peer_cfg_create(name, this->ike->get_ref(this->ike), &peer);
182
183 auth = auth_cfg_create();
184 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
185 auth->add(auth, AUTH_RULE_IDENTITY,
186 identification_create_from_encoding(ID_KEY_ID, me));
187 peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
188 auth = auth_cfg_create();
189 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
190 auth->add(auth, AUTH_RULE_IDENTITY,
191 identification_create_from_encoding(ID_KEY_ID, other));
192 peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
193
194 child_cfg = child_cfg_create(name, &child);
195 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
196 child_cfg->add_proposal(child_cfg, proposal_create_default_aead(PROTO_ESP));
197 child_cfg->add_traffic_selector(child_cfg, TRUE, ts_from_string(local_net));
198 child_cfg->add_traffic_selector(child_cfg, FALSE, ts_from_string(remote_net));
199 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
200 e->destroy(e);
201 return peer_cfg;
202 }
203
204 METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*,
205 private_medcli_config_t *this, host_t *me, host_t *other)
206 {
207 return enumerator_create_single(this->ike, NULL);
208 }
209
210 typedef struct {
211 /** implements enumerator */
212 enumerator_t public;
213 /** inner SQL enumerator */
214 enumerator_t *inner;
215 /** currently enumerated peer config */
216 peer_cfg_t *current;
217 /** ike cfg to use in peer cfg */
218 ike_cfg_t *ike;
219 /** rekey time */
220 int rekey;
221 /** dpd time */
222 int dpd;
223 } peer_enumerator_t;
224
225 METHOD(enumerator_t, peer_enumerator_enumerate, bool,
226 peer_enumerator_t *this, va_list args)
227 {
228 char *name, *local_net, *remote_net;
229 chunk_t me, other;
230 peer_cfg_t **cfg;
231 child_cfg_t *child_cfg;
232 auth_cfg_t *auth;
233 peer_cfg_create_t peer = {
234 .cert_policy = CERT_NEVER_SEND,
235 .unique = UNIQUE_REPLACE,
236 .keyingtries = 1,
237 .rekey_time = this->rekey * 60,
238 .jitter_time = this->rekey * 5,
239 .over_time = this->rekey * 3,
240 .dpd = this->dpd,
241 };
242 child_cfg_create_t child = {
243 .lifetime = {
244 .time = {
245 .life = this->rekey * 60 + this->rekey,
246 .rekey = this->rekey,
247 .jitter = this->rekey
248 },
249 },
250 .mode = MODE_TUNNEL,
251 };
252
253 VA_ARGS_VGET(args, cfg);
254
255 DESTROY_IF(this->current);
256 if (!this->inner->enumerate(this->inner, &name, &me, &other,
257 &local_net, &remote_net))
258 {
259 this->current = NULL;
260 return FALSE;
261 }
262 this->current = peer_cfg_create(name, this->ike->get_ref(this->ike), &peer);
263
264 auth = auth_cfg_create();
265 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
266 auth->add(auth, AUTH_RULE_IDENTITY,
267 identification_create_from_encoding(ID_KEY_ID, me));
268 this->current->add_auth_cfg(this->current, auth, TRUE);
269 auth = auth_cfg_create();
270 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
271 auth->add(auth, AUTH_RULE_IDENTITY,
272 identification_create_from_encoding(ID_KEY_ID, other));
273 this->current->add_auth_cfg(this->current, auth, FALSE);
274
275 child_cfg = child_cfg_create(name, &child);
276 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
277 child_cfg->add_proposal(child_cfg, proposal_create_default_aead(PROTO_ESP));
278 child_cfg->add_traffic_selector(child_cfg, TRUE, ts_from_string(local_net));
279 child_cfg->add_traffic_selector(child_cfg, FALSE, ts_from_string(remote_net));
280 this->current->add_child_cfg(this->current, child_cfg);
281 *cfg = this->current;
282 return TRUE;
283 }
284
285 METHOD(enumerator_t, peer_enumerator_destroy, void,
286 peer_enumerator_t *this)
287 {
288 DESTROY_IF(this->current);
289 this->inner->destroy(this->inner);
290 free(this);
291 }
292
293 METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
294 private_medcli_config_t *this, identification_t *me,
295 identification_t *other)
296 {
297 peer_enumerator_t *e;
298
299 INIT(e,
300 .public = {
301 .enumerate = enumerator_enumerate_default,
302 .venumerate = _peer_enumerator_enumerate,
303 .destroy = _peer_enumerator_destroy,
304 },
305 .ike = this->ike,
306 .rekey = this->rekey,
307 .dpd = this->dpd,
308 );
309
310 /* filter on IDs: NULL or ANY or matching KEY_ID */
311 e->inner = this->db->query(this->db,
312 "SELECT Alias, ClientConfig.KeyId, Connection.KeyId, "
313 "Connection.LocalSubnet, Connection.RemoteSubnet "
314 "FROM ClientConfig JOIN Connection "
315 "WHERE Active AND "
316 "(? OR ClientConfig.KeyId = ?) AND (? OR Connection.KeyId = ?)",
317 DB_INT, me == NULL || me->get_type(me) == ID_ANY,
318 DB_BLOB, me && me->get_type(me) == ID_KEY_ID ?
319 me->get_encoding(me) : chunk_empty,
320 DB_INT, other == NULL || other->get_type(other) == ID_ANY,
321 DB_BLOB, other && other->get_type(other) == ID_KEY_ID ?
322 other->get_encoding(other) : chunk_empty,
323 DB_TEXT, DB_BLOB, DB_BLOB, DB_TEXT, DB_TEXT);
324 if (!e->inner)
325 {
326 free(e);
327 return NULL;
328 }
329 return &e->public;
330 }
331
332 /**
333 * initiate a peer config
334 */
335 static job_requeue_t initiate_config(peer_cfg_t *peer_cfg)
336 {
337 enumerator_t *enumerator;
338 child_cfg_t *child_cfg = NULL;;
339
340 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
341 enumerator->enumerate(enumerator, &child_cfg);
342 if (child_cfg)
343 {
344 child_cfg->get_ref(child_cfg);
345 peer_cfg->get_ref(peer_cfg);
346 enumerator->destroy(enumerator);
347 charon->controller->initiate(charon->controller,
348 peer_cfg, child_cfg, NULL, NULL, 0, FALSE);
349 }
350 else
351 {
352 enumerator->destroy(enumerator);
353 }
354 return JOB_REQUEUE_NONE;
355 }
356
357 /**
358 * schedule initiation of all "active" connections
359 */
360 static void schedule_autoinit(private_medcli_config_t *this)
361 {
362 enumerator_t *e;
363 char *name;
364
365 e = this->db->query(this->db, "SELECT Alias FROM Connection WHERE Active",
366 DB_TEXT);
367 if (e)
368 {
369 while (e->enumerate(e, &name))
370 {
371 peer_cfg_t *peer_cfg;
372
373 peer_cfg = get_peer_cfg_by_name(this, name);
374 if (peer_cfg)
375 {
376 /* schedule asynchronous initiation job */
377 lib->processor->queue_job(lib->processor,
378 (job_t*)callback_job_create(
379 (callback_job_cb_t)initiate_config,
380 peer_cfg, (void*)peer_cfg->destroy, NULL));
381 }
382 }
383 e->destroy(e);
384 }
385 }
386
387 METHOD(medcli_config_t, destroy, void,
388 private_medcli_config_t *this)
389 {
390 this->ike->destroy(this->ike);
391 free(this);
392 }
393
394 /**
395 * Described in header.
396 */
397 medcli_config_t *medcli_config_create(database_t *db)
398 {
399 private_medcli_config_t *this;
400
401 INIT(this,
402 .public = {
403 .backend = {
404 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
405 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
406 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
407 },
408 .destroy = _destroy,
409 },
410 .db = db,
411 .rekey = lib->settings->get_time(lib->settings, "medcli.rekey", 1200),
412 .dpd = lib->settings->get_time(lib->settings, "medcli.dpd", 300),
413 .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0",
414 charon->socket->get_port(charon->socket, FALSE),
415 "0.0.0.0", IKEV2_UDP_PORT,
416 FRAGMENTATION_NO, 0),
417 );
418 this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE));
419 this->ike->add_proposal(this->ike, proposal_create_default_aead(PROTO_IKE));
420
421 schedule_autoinit(this);
422
423 return &this->public;
424 }