]>
Commit | Line | Data |
---|---|---|
b3d8bd8d MW |
1 | /* |
2 | * Copyright (C) 2014 Martin Willi | |
3 | * Copyright (C) 2014 revosec AG | |
4 | * | |
f927ba97 | 5 | * Copyright (C) 2015-2017 Tobias Brunner |
3f1de986 | 6 | * Copyright (C) 2015-2016 Andreas Steffen |
e1943491 AS |
7 | * HSR Hochschule fuer Technik Rapperswil |
8 | * | |
b3d8bd8d MW |
9 | * This program is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | * for more details. | |
18 | */ | |
19 | ||
17d34356 TT |
20 | /* |
21 | * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi> | |
22 | * | |
23 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
24 | * of this software and associated documentation files (the "Software"), to deal | |
25 | * in the Software without restriction, including without limitation the rights | |
26 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
27 | * copies of the Software, and to permit persons to whom the Software is | |
28 | * furnished to do so, subject to the following conditions: | |
29 | * | |
30 | * The above copyright notice and this permission notice shall be included in | |
31 | * all copies or substantial portions of the Software. | |
32 | * | |
33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
39 | * THE SOFTWARE. | |
40 | */ | |
41 | ||
b3d8bd8d MW |
42 | #define _GNU_SOURCE |
43 | ||
44 | #include "vici_config.h" | |
45 | #include "vici_builder.h" | |
46 | ||
47 | #include <daemon.h> | |
48 | #include <threading/rwlock.h> | |
20df9d31 | 49 | #include <threading/rwlock_condvar.h> |
7de35b7f | 50 | #include <collections/array.h> |
b3d8bd8d MW |
51 | #include <collections/linked_list.h> |
52 | ||
87371460 AS |
53 | #include <pubkey_cert.h> |
54 | ||
b3d8bd8d | 55 | #include <stdio.h> |
b3d8bd8d | 56 | |
c5205105 MW |
57 | /** |
58 | * Magic value for an undefined lifetime | |
59 | */ | |
b12c53ce | 60 | #define LFT_UNDEFINED (~(uint64_t)0) |
c5205105 | 61 | |
046befec MW |
62 | /** |
63 | * Default IKE rekey time | |
64 | */ | |
b1df6312 | 65 | #define LFT_DEFAULT_IKE_REKEY_TIME (4 * 60 * 60) |
046befec | 66 | |
58581447 MW |
67 | /** |
68 | * Default CHILD rekey time | |
69 | */ | |
b1df6312 AS |
70 | #define LFT_DEFAULT_CHILD_REKEY_TIME (1 * 60 * 60) |
71 | ||
72 | /** | |
73 | * Default CHILD rekey bytes | |
74 | */ | |
75 | #define LFT_DEFAULT_CHILD_REKEY_BYTES 0 | |
76 | ||
77 | /** | |
78 | * Default CHILD rekey packets | |
79 | */ | |
80 | #define LFT_DEFAULT_CHILD_REKEY_PACKETS 0 | |
58581447 | 81 | |
d73a4617 MW |
82 | /** |
83 | * Undefined replay window | |
84 | */ | |
b12c53ce | 85 | #define REPLAY_UNDEFINED (~(uint32_t)0) |
d73a4617 | 86 | |
b3d8bd8d MW |
87 | typedef struct private_vici_config_t private_vici_config_t; |
88 | ||
89 | /** | |
90 | * Private data of an vici_config_t object. | |
91 | */ | |
92 | struct private_vici_config_t { | |
93 | ||
94 | /** | |
95 | * Public vici_config_t interface. | |
96 | */ | |
97 | vici_config_t public; | |
98 | ||
99 | /** | |
100 | * Dispatcher | |
101 | */ | |
102 | vici_dispatcher_t *dispatcher; | |
103 | ||
104 | /** | |
105 | * List of loaded connections, as peer_cfg_t | |
106 | */ | |
107 | linked_list_t *conns; | |
108 | ||
109 | /** | |
110 | * Lock for conns list | |
111 | */ | |
112 | rwlock_t *lock; | |
63d37038 | 113 | |
20df9d31 TB |
114 | /** |
115 | * Condvar used to snyc running actions | |
116 | */ | |
117 | rwlock_condvar_t *condvar; | |
118 | ||
119 | /** | |
120 | * True while we run or undo a start action | |
121 | */ | |
122 | bool handling_actions; | |
123 | ||
87371460 AS |
124 | /** |
125 | * Credential backend managed by VICI used for our certificates | |
126 | */ | |
127 | vici_cred_t *cred; | |
128 | ||
63d37038 AS |
129 | /** |
130 | * Auxiliary certification authority information | |
131 | */ | |
132 | vici_authority_t *authority; | |
133 | ||
b3d8bd8d MW |
134 | }; |
135 | ||
136 | METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, | |
137 | private_vici_config_t *this, identification_t *me, identification_t *other) | |
138 | { | |
139 | this->lock->read_lock(this->lock); | |
140 | return enumerator_create_cleaner(this->conns->create_enumerator(this->conns), | |
141 | (void*)this->lock->unlock, this->lock); | |
142 | } | |
143 | ||
144 | /** | |
145 | * Enumerator filter function for ike configs | |
146 | */ | |
147 | static bool ike_filter(void *data, peer_cfg_t **in, ike_cfg_t **out) | |
148 | { | |
149 | *out = (*in)->get_ike_cfg(*in); | |
150 | return TRUE; | |
151 | } | |
152 | ||
153 | METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*, | |
154 | private_vici_config_t *this, host_t *me, host_t *other) | |
155 | { | |
156 | this->lock->read_lock(this->lock); | |
157 | return enumerator_create_filter(this->conns->create_enumerator(this->conns), | |
158 | (void*)ike_filter, this->lock, | |
159 | (void*)this->lock->unlock); | |
160 | } | |
161 | ||
162 | METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, | |
163 | private_vici_config_t *this, char *name) | |
164 | { | |
165 | peer_cfg_t *current, *found = NULL; | |
166 | enumerator_t *enumerator; | |
167 | ||
168 | this->lock->read_lock(this->lock); | |
169 | enumerator = this->conns->create_enumerator(this->conns); | |
170 | while (enumerator->enumerate(enumerator, ¤t)) | |
171 | { | |
172 | if (streq(current->get_name(current), name)) | |
173 | { | |
174 | found = current; | |
175 | found->get_ref(found); | |
176 | break; | |
177 | } | |
178 | } | |
179 | enumerator->destroy(enumerator); | |
180 | this->lock->unlock(this->lock); | |
181 | ||
182 | return found; | |
183 | } | |
184 | ||
185 | /** | |
186 | * Create a (error) reply message | |
187 | */ | |
188 | static vici_message_t* create_reply(char *fmt, ...) | |
189 | { | |
190 | vici_builder_t *builder; | |
191 | va_list args; | |
192 | ||
193 | builder = vici_builder_create(); | |
194 | builder->add_kv(builder, "success", fmt ? "no" : "yes"); | |
195 | if (fmt) | |
196 | { | |
197 | va_start(args, fmt); | |
198 | builder->vadd_kv(builder, "errmsg", fmt, args); | |
199 | va_end(args); | |
200 | } | |
201 | return builder->finalize(builder); | |
202 | } | |
203 | ||
204 | /** | |
205 | * A rule to parse a key/value or list item | |
206 | */ | |
207 | typedef struct { | |
208 | /** name of the key/value or list */ | |
209 | char *name; | |
210 | /** function to parse value */ | |
211 | bool (*parse)(void *out, chunk_t value); | |
212 | /** result, passed to parse() */ | |
213 | void *out; | |
214 | } parse_rule_t; | |
215 | ||
216 | /** | |
217 | * Parse key/values using a rule-set | |
218 | */ | |
219 | static bool parse_rules(parse_rule_t *rules, int count, char *name, | |
220 | chunk_t value, vici_message_t **reply) | |
221 | { | |
222 | int i; | |
223 | ||
224 | for (i = 0; i < count; i++) | |
225 | { | |
226 | if (streq(name, rules[i].name)) | |
227 | { | |
228 | if (rules[i].parse(rules[i].out, value)) | |
229 | { | |
230 | return TRUE; | |
231 | } | |
232 | *reply = create_reply("invalid value for: %s, config discarded", | |
233 | name); | |
234 | return FALSE; | |
235 | } | |
236 | } | |
237 | *reply = create_reply("unknown option: %s, config discarded", name); | |
238 | return FALSE; | |
239 | } | |
240 | ||
241 | /** | |
242 | * Parse callback data, passed to each callback | |
243 | */ | |
244 | typedef struct { | |
245 | private_vici_config_t *this; | |
246 | vici_message_t *reply; | |
247 | } request_data_t; | |
248 | ||
00bf6a2a TB |
249 | /** |
250 | * Certificate data | |
251 | */ | |
252 | typedef struct { | |
253 | request_data_t *request; | |
254 | char *handle; | |
255 | uint32_t slot; | |
256 | char *module; | |
2f8354ca | 257 | char *file; |
00bf6a2a TB |
258 | } cert_data_t; |
259 | ||
260 | /** | |
261 | * Clean up certificate data | |
262 | */ | |
263 | static void free_cert_data(cert_data_t *data) | |
264 | { | |
265 | free(data->handle); | |
266 | free(data->module); | |
2f8354ca | 267 | free(data->file); |
00bf6a2a TB |
268 | free(data); |
269 | } | |
270 | ||
229cdf6b TB |
271 | /** |
272 | * Auth config data | |
273 | */ | |
274 | typedef struct { | |
275 | request_data_t *request; | |
276 | auth_cfg_t *cfg; | |
b12c53ce | 277 | uint32_t round; |
229cdf6b TB |
278 | } auth_data_t; |
279 | ||
280 | /** | |
281 | * Clean up auth config data | |
282 | */ | |
283 | static void free_auth_data(auth_data_t *data) | |
284 | { | |
285 | DESTROY_IF(data->cfg); | |
286 | free(data); | |
287 | } | |
288 | ||
b3d8bd8d MW |
289 | /** |
290 | * Data associated to a peer config | |
291 | */ | |
292 | typedef struct { | |
293 | request_data_t *request; | |
b12c53ce | 294 | uint32_t version; |
b3d8bd8d MW |
295 | bool aggressive; |
296 | bool encap; | |
297 | bool mobike; | |
298 | bool send_certreq; | |
299 | bool pull; | |
300 | cert_policy_t send_cert; | |
b12c53ce AS |
301 | uint64_t dpd_delay; |
302 | uint64_t dpd_timeout; | |
b3d8bd8d MW |
303 | fragmentation_t fragmentation; |
304 | unique_policy_t unique; | |
b12c53ce AS |
305 | uint32_t keyingtries; |
306 | uint32_t local_port; | |
307 | uint32_t remote_port; | |
b3d8bd8d MW |
308 | char *local_addrs; |
309 | char *remote_addrs; | |
310 | linked_list_t *local; | |
311 | linked_list_t *remote; | |
312 | linked_list_t *proposals; | |
313 | linked_list_t *children; | |
314 | linked_list_t *vips; | |
afb8f492 | 315 | char *pools; |
b12c53ce AS |
316 | uint64_t reauth_time; |
317 | uint64_t rekey_time; | |
318 | uint64_t over_time; | |
319 | uint64_t rand_time; | |
44fcc833 | 320 | uint8_t dscp; |
f927ba97 TB |
321 | #ifdef ME |
322 | bool mediation; | |
323 | char *mediated_by; | |
324 | identification_t *peer_id; | |
325 | #endif /* ME */ | |
b3d8bd8d MW |
326 | } peer_data_t; |
327 | ||
328 | /** | |
329 | * Log relevant auth config data | |
330 | */ | |
331 | static void log_auth(auth_cfg_t *auth) | |
332 | { | |
333 | enumerator_t *enumerator; | |
334 | auth_rule_t rule; | |
335 | union { | |
336 | uintptr_t u; | |
337 | identification_t *id; | |
338 | char *str; | |
339 | } v; | |
340 | ||
341 | enumerator = auth->create_enumerator(auth); | |
342 | while (enumerator->enumerate(enumerator, &rule, &v)) | |
343 | { | |
344 | switch (rule) | |
345 | { | |
346 | case AUTH_RULE_AUTH_CLASS: | |
347 | DBG2(DBG_CFG, " class = %N", auth_class_names, v.u); | |
348 | break; | |
349 | case AUTH_RULE_EAP_TYPE: | |
350 | DBG2(DBG_CFG, " eap-type = %N", eap_type_names, v.u); | |
351 | break; | |
352 | case AUTH_RULE_EAP_VENDOR: | |
353 | DBG2(DBG_CFG, " eap-vendor = %u", v.u); | |
354 | break; | |
355 | case AUTH_RULE_XAUTH_BACKEND: | |
356 | DBG2(DBG_CFG, " xauth = %s", v.str); | |
357 | break; | |
358 | case AUTH_RULE_CRL_VALIDATION: | |
359 | DBG2(DBG_CFG, " revocation = %N", cert_validation_names, v.u); | |
360 | break; | |
361 | case AUTH_RULE_IDENTITY: | |
362 | DBG2(DBG_CFG, " id = %Y", v.id); | |
363 | break; | |
364 | case AUTH_RULE_AAA_IDENTITY: | |
365 | DBG2(DBG_CFG, " aaa_id = %Y", v.id); | |
366 | break; | |
367 | case AUTH_RULE_EAP_IDENTITY: | |
368 | DBG2(DBG_CFG, " eap_id = %Y", v.id); | |
369 | break; | |
370 | case AUTH_RULE_XAUTH_IDENTITY: | |
371 | DBG2(DBG_CFG, " xauth_id = %Y", v.id); | |
372 | break; | |
e6e975ff MW |
373 | case AUTH_RULE_GROUP: |
374 | DBG2(DBG_CFG, " group = %Y", v.id); | |
375 | break; | |
b3d8bd8d MW |
376 | default: |
377 | break; | |
378 | } | |
379 | } | |
380 | enumerator->destroy(enumerator); | |
381 | } | |
382 | ||
383 | /** | |
384 | * Log parsed peer data | |
385 | */ | |
386 | static void log_peer_data(peer_data_t *data) | |
387 | { | |
388 | enumerator_t *enumerator; | |
229cdf6b | 389 | auth_data_t *auth; |
b3d8bd8d MW |
390 | host_t *host; |
391 | ||
392 | DBG2(DBG_CFG, " version = %u", data->version); | |
393 | DBG2(DBG_CFG, " local_addrs = %s", data->local_addrs); | |
394 | DBG2(DBG_CFG, " remote_addrs = %s", data->remote_addrs); | |
395 | DBG2(DBG_CFG, " local_port = %u", data->local_port); | |
396 | DBG2(DBG_CFG, " remote_port = %u", data->remote_port); | |
397 | DBG2(DBG_CFG, " send_certreq = %u", data->send_certreq); | |
398 | DBG2(DBG_CFG, " send_cert = %N", cert_policy_names, data->send_cert); | |
399 | DBG2(DBG_CFG, " mobike = %u", data->mobike); | |
400 | DBG2(DBG_CFG, " aggressive = %u", data->aggressive); | |
44fcc833 | 401 | DBG2(DBG_CFG, " dscp = 0x%.2x", data->dscp); |
b3d8bd8d MW |
402 | DBG2(DBG_CFG, " encap = %u", data->encap); |
403 | DBG2(DBG_CFG, " dpd_delay = %llu", data->dpd_delay); | |
404 | DBG2(DBG_CFG, " dpd_timeout = %llu", data->dpd_timeout); | |
405 | DBG2(DBG_CFG, " fragmentation = %u", data->fragmentation); | |
406 | DBG2(DBG_CFG, " unique = %N", unique_policy_names, data->unique); | |
407 | DBG2(DBG_CFG, " keyingtries = %u", data->keyingtries); | |
408 | DBG2(DBG_CFG, " reauth_time = %llu", data->reauth_time); | |
409 | DBG2(DBG_CFG, " rekey_time = %llu", data->rekey_time); | |
410 | DBG2(DBG_CFG, " over_time = %llu", data->over_time); | |
411 | DBG2(DBG_CFG, " rand_time = %llu", data->rand_time); | |
412 | DBG2(DBG_CFG, " proposals = %#P", data->proposals); | |
f927ba97 TB |
413 | #ifdef ME |
414 | DBG2(DBG_CFG, " mediation = %u", data->mediation); | |
415 | if (data->mediated_by) | |
416 | { | |
417 | DBG2(DBG_CFG, " mediated_by = %s", data->mediated_by); | |
418 | DBG2(DBG_CFG, " mediation_peer = %Y", data->peer_id); | |
419 | } | |
420 | #endif /* ME */ | |
b3d8bd8d MW |
421 | |
422 | if (data->vips->get_count(data->vips)) | |
423 | { | |
424 | DBG2(DBG_CFG, " vips:"); | |
425 | } | |
426 | enumerator = data->vips->create_enumerator(data->vips); | |
427 | while (enumerator->enumerate(enumerator, &host)) | |
428 | { | |
429 | DBG2(DBG_CFG, " %H", host); | |
430 | } | |
431 | enumerator->destroy(enumerator); | |
432 | ||
433 | enumerator = data->local->create_enumerator(data->local); | |
434 | while (enumerator->enumerate(enumerator, &auth)) | |
435 | { | |
436 | DBG2(DBG_CFG, " local:"); | |
229cdf6b | 437 | log_auth(auth->cfg); |
b3d8bd8d MW |
438 | } |
439 | enumerator->destroy(enumerator); | |
440 | ||
441 | enumerator = data->remote->create_enumerator(data->remote); | |
442 | while (enumerator->enumerate(enumerator, &auth)) | |
443 | { | |
444 | DBG2(DBG_CFG, " remote:"); | |
229cdf6b | 445 | log_auth(auth->cfg); |
b3d8bd8d MW |
446 | } |
447 | enumerator->destroy(enumerator); | |
448 | } | |
449 | ||
450 | /** | |
451 | * Clean up peer config data | |
452 | */ | |
453 | static void free_peer_data(peer_data_t *data) | |
454 | { | |
229cdf6b TB |
455 | data->local->destroy_function(data->local, (void*)free_auth_data); |
456 | data->remote->destroy_function(data->remote, (void*)free_auth_data); | |
b3d8bd8d MW |
457 | data->children->destroy_offset(data->children, |
458 | offsetof(child_cfg_t, destroy)); | |
459 | data->proposals->destroy_offset(data->proposals, | |
460 | offsetof(proposal_t, destroy)); | |
461 | data->vips->destroy_offset(data->vips, offsetof(host_t, destroy)); | |
afb8f492 | 462 | free(data->pools); |
b3d8bd8d MW |
463 | free(data->local_addrs); |
464 | free(data->remote_addrs); | |
f927ba97 TB |
465 | #ifdef ME |
466 | free(data->mediated_by); | |
467 | DESTROY_IF(data->peer_id); | |
468 | #endif /* ME */ | |
b3d8bd8d MW |
469 | } |
470 | ||
471 | /** | |
472 | * CHILD config data | |
473 | */ | |
474 | typedef struct { | |
475 | request_data_t *request; | |
b3d8bd8d MW |
476 | linked_list_t *proposals; |
477 | linked_list_t *local_ts; | |
478 | linked_list_t *remote_ts; | |
8a00a845 TB |
479 | uint32_t replay_window; |
480 | bool policies; | |
50721a61 | 481 | bool policies_fwd_out; |
8a00a845 | 482 | child_cfg_create_t cfg; |
b3d8bd8d MW |
483 | } child_data_t; |
484 | ||
485 | /** | |
486 | * Log parsed CHILD config data | |
487 | */ | |
488 | static void log_child_data(child_data_t *data, char *name) | |
489 | { | |
8a00a845 TB |
490 | child_cfg_create_t *cfg = &data->cfg; |
491 | ||
b3d8bd8d | 492 | DBG2(DBG_CFG, " child %s:", name); |
8a00a845 TB |
493 | DBG2(DBG_CFG, " rekey_time = %llu", cfg->lifetime.time.rekey); |
494 | DBG2(DBG_CFG, " life_time = %llu", cfg->lifetime.time.life); | |
495 | DBG2(DBG_CFG, " rand_time = %llu", cfg->lifetime.time.jitter); | |
496 | DBG2(DBG_CFG, " rekey_bytes = %llu", cfg->lifetime.bytes.rekey); | |
497 | DBG2(DBG_CFG, " life_bytes = %llu", cfg->lifetime.bytes.life); | |
498 | DBG2(DBG_CFG, " rand_bytes = %llu", cfg->lifetime.bytes.jitter); | |
499 | DBG2(DBG_CFG, " rekey_packets = %llu", cfg->lifetime.packets.rekey); | |
500 | DBG2(DBG_CFG, " life_packets = %llu", cfg->lifetime.packets.life); | |
501 | DBG2(DBG_CFG, " rand_packets = %llu", cfg->lifetime.packets.jitter); | |
502 | DBG2(DBG_CFG, " updown = %s", cfg->updown); | |
503 | DBG2(DBG_CFG, " hostaccess = %u", cfg->hostaccess); | |
504 | DBG2(DBG_CFG, " ipcomp = %u", cfg->ipcomp); | |
3bedf10b TB |
505 | DBG2(DBG_CFG, " mode = %N%s", ipsec_mode_names, cfg->mode, |
506 | cfg->proxy_mode ? "_PROXY" : ""); | |
9322e5b3 | 507 | DBG2(DBG_CFG, " policies = %u", data->policies); |
50721a61 | 508 | DBG2(DBG_CFG, " policies_fwd_out = %u", data->policies_fwd_out); |
d73a4617 MW |
509 | if (data->replay_window != REPLAY_UNDEFINED) |
510 | { | |
511 | DBG2(DBG_CFG, " replay_window = %u", data->replay_window); | |
512 | } | |
8a00a845 TB |
513 | DBG2(DBG_CFG, " dpd_action = %N", action_names, cfg->dpd_action); |
514 | DBG2(DBG_CFG, " start_action = %N", action_names, cfg->start_action); | |
515 | DBG2(DBG_CFG, " close_action = %N", action_names, cfg->close_action); | |
516 | DBG2(DBG_CFG, " reqid = %u", cfg->reqid); | |
517 | DBG2(DBG_CFG, " tfc = %d", cfg->tfc); | |
7f57c4f9 | 518 | DBG2(DBG_CFG, " priority = %d", cfg->priority); |
c26e4330 | 519 | DBG2(DBG_CFG, " interface = %s", cfg->interface); |
b3d8bd8d | 520 | DBG2(DBG_CFG, " mark_in = %u/%u", |
8a00a845 | 521 | cfg->mark_in.value, cfg->mark_in.mask); |
b3d8bd8d | 522 | DBG2(DBG_CFG, " mark_out = %u/%u", |
8a00a845 TB |
523 | cfg->mark_out.value, cfg->mark_out.mask); |
524 | DBG2(DBG_CFG, " inactivity = %llu", cfg->inactivity); | |
b3d8bd8d MW |
525 | DBG2(DBG_CFG, " proposals = %#P", data->proposals); |
526 | DBG2(DBG_CFG, " local_ts = %#R", data->local_ts); | |
527 | DBG2(DBG_CFG, " remote_ts = %#R", data->remote_ts); | |
528 | } | |
529 | ||
530 | /** | |
531 | * Clean up CHILD config data | |
532 | */ | |
533 | static void free_child_data(child_data_t *data) | |
534 | { | |
535 | data->proposals->destroy_offset(data->proposals, | |
536 | offsetof(proposal_t, destroy)); | |
537 | data->local_ts->destroy_offset(data->local_ts, | |
538 | offsetof(traffic_selector_t, destroy)); | |
539 | data->remote_ts->destroy_offset(data->remote_ts, | |
540 | offsetof(traffic_selector_t, destroy)); | |
8a00a845 | 541 | free(data->cfg.updown); |
c26e4330 | 542 | free(data->cfg.interface); |
b3d8bd8d MW |
543 | } |
544 | ||
b3d8bd8d MW |
545 | /** |
546 | * Common proposal parsing | |
547 | */ | |
548 | static bool parse_proposal(linked_list_t *list, protocol_id_t proto, chunk_t v) | |
549 | { | |
7f65a8c2 | 550 | char buf[BUF_LEN]; |
b3d8bd8d MW |
551 | proposal_t *proposal; |
552 | ||
553 | if (!vici_stringify(v, buf, sizeof(buf))) | |
554 | { | |
555 | return FALSE; | |
556 | } | |
557 | if (strcaseeq("default", buf)) | |
558 | { | |
559 | proposal = proposal_create_default(proto); | |
879e3d12 MW |
560 | if (proposal) |
561 | { | |
562 | list->insert_last(list, proposal); | |
563 | } | |
564 | proposal = proposal_create_default_aead(proto); | |
565 | if (proposal) | |
566 | { | |
567 | list->insert_last(list, proposal); | |
568 | } | |
569 | return TRUE; | |
b3d8bd8d | 570 | } |
879e3d12 MW |
571 | proposal = proposal_create_from_string(proto, buf); |
572 | if (proposal) | |
b3d8bd8d | 573 | { |
879e3d12 MW |
574 | list->insert_last(list, proposal); |
575 | return TRUE; | |
b3d8bd8d | 576 | } |
879e3d12 | 577 | return FALSE; |
b3d8bd8d MW |
578 | } |
579 | ||
580 | /** | |
581 | * Parse IKE proposal | |
582 | */ | |
583 | CALLBACK(parse_ike_proposal, bool, | |
584 | linked_list_t *out, chunk_t v) | |
585 | { | |
586 | return parse_proposal(out, PROTO_IKE, v); | |
587 | } | |
588 | ||
589 | /** | |
590 | * Parse ESP proposal | |
591 | */ | |
592 | CALLBACK(parse_esp_proposal, bool, | |
593 | linked_list_t *out, chunk_t v) | |
594 | { | |
595 | return parse_proposal(out, PROTO_ESP, v); | |
596 | } | |
597 | ||
598 | /** | |
599 | * Parse AH proposal | |
600 | */ | |
601 | CALLBACK(parse_ah_proposal, bool, | |
602 | linked_list_t *out, chunk_t v) | |
603 | { | |
604 | return parse_proposal(out, PROTO_AH, v); | |
605 | } | |
606 | ||
607 | /** | |
608 | * Parse a traffic selector | |
609 | */ | |
610 | CALLBACK(parse_ts, bool, | |
611 | linked_list_t *out, chunk_t v) | |
612 | { | |
7f65a8c2 | 613 | char buf[BUF_LEN], *protoport, *sep, *port = "", *end; |
35babdf4 | 614 | traffic_selector_t *ts = NULL; |
b3d8bd8d MW |
615 | struct protoent *protoent; |
616 | struct servent *svc; | |
617 | long int p; | |
b12c53ce AS |
618 | uint16_t from = 0, to = 0xffff; |
619 | uint8_t proto = 0; | |
b3d8bd8d MW |
620 | |
621 | if (!vici_stringify(v, buf, sizeof(buf))) | |
622 | { | |
623 | return FALSE; | |
624 | } | |
625 | ||
626 | protoport = strchr(buf, '['); | |
627 | if (protoport) | |
628 | { | |
629 | *(protoport++) = '\0'; | |
630 | ||
631 | sep = strrchr(protoport, ']'); | |
632 | if (!sep) | |
633 | { | |
634 | return FALSE; | |
635 | } | |
636 | *sep = '\0'; | |
637 | ||
638 | sep = strchr(protoport, '/'); | |
639 | if (sep) | |
640 | { /* protocol/port */ | |
641 | *sep = '\0'; | |
642 | port = sep + 1; | |
643 | } | |
644 | ||
645 | if (streq(protoport, "any")) | |
646 | { | |
647 | proto = 0; | |
648 | } | |
649 | else | |
650 | { | |
651 | protoent = getprotobyname(protoport); | |
652 | if (protoent) | |
653 | { | |
654 | proto = protoent->p_proto; | |
655 | } | |
656 | else | |
657 | { | |
658 | p = strtol(protoport, &end, 0); | |
659 | if ((*protoport && *end) || p < 0 || p > 0xff) | |
660 | { | |
661 | return FALSE; | |
662 | } | |
b12c53ce | 663 | proto = (uint8_t)p; |
b3d8bd8d MW |
664 | } |
665 | } | |
666 | if (streq(port, "opaque")) | |
667 | { | |
668 | from = 0xffff; | |
669 | to = 0; | |
670 | } | |
671 | else if (*port && !streq(port, "any")) | |
672 | { | |
673 | svc = getservbyname(port, NULL); | |
674 | if (svc) | |
675 | { | |
676 | from = to = ntohs(svc->s_port); | |
677 | } | |
678 | else | |
679 | { | |
680 | p = strtol(port, &end, 0); | |
681 | if (p < 0 || p > 0xffff) | |
682 | { | |
683 | return FALSE; | |
684 | } | |
685 | from = p; | |
686 | if (*end == '-') | |
687 | { | |
688 | port = end + 1; | |
689 | p = strtol(port, &end, 0); | |
690 | if (p < 0 || p > 0xffff) | |
691 | { | |
692 | return FALSE; | |
693 | } | |
694 | } | |
695 | to = p; | |
696 | if (*end) | |
697 | { | |
698 | return FALSE; | |
699 | } | |
700 | } | |
701 | } | |
702 | } | |
703 | if (streq(buf, "dynamic")) | |
704 | { | |
705 | ts = traffic_selector_create_dynamic(proto, from, to); | |
706 | } | |
3f1de986 AS |
707 | else if (strchr(buf, '-')) |
708 | { | |
709 | host_t *lower, *upper; | |
710 | ts_type_t type; | |
711 | ||
712 | if (host_create_from_range(buf, &lower, &upper)) | |
713 | { | |
714 | type = (lower->get_family(lower) == AF_INET) ? | |
715 | TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; | |
716 | ts = traffic_selector_create_from_bytes(proto, type, | |
717 | lower->get_address(lower), from, | |
718 | upper->get_address(upper), to); | |
719 | lower->destroy(lower); | |
720 | upper->destroy(upper); | |
721 | } | |
722 | } | |
b3d8bd8d MW |
723 | else |
724 | { | |
725 | ts = traffic_selector_create_from_cidr(buf, proto, from, to); | |
726 | } | |
727 | if (!ts) | |
728 | { | |
729 | return FALSE; | |
730 | } | |
731 | out->insert_last(out, ts); | |
732 | return TRUE; | |
733 | } | |
734 | ||
735 | /** | |
736 | * Parse a string | |
737 | */ | |
738 | CALLBACK(parse_string, bool, | |
739 | char **out, chunk_t v) | |
740 | { | |
741 | if (!chunk_printable(v, NULL, ' ')) | |
742 | { | |
743 | return FALSE; | |
744 | } | |
745 | free(*out); | |
746 | *out = NULL; | |
747 | if (asprintf(out, "%.*s", (int)v.len, v.ptr) == -1) | |
748 | { | |
749 | return FALSE; | |
750 | } | |
751 | return TRUE; | |
752 | } | |
753 | ||
754 | /** | |
755 | * Map a string to an integer | |
756 | */ | |
757 | typedef struct { | |
758 | char *str; | |
759 | int d; | |
760 | } enum_map_t; | |
761 | ||
762 | /** | |
763 | * Parse a string to an integer mapping | |
764 | */ | |
765 | static bool parse_map(enum_map_t *map, int count, int *out, chunk_t v) | |
766 | { | |
7f65a8c2 | 767 | char buf[BUF_LEN]; |
b3d8bd8d MW |
768 | int i; |
769 | ||
770 | if (!vici_stringify(v, buf, sizeof(buf))) | |
771 | { | |
772 | return FALSE; | |
773 | } | |
774 | for (i = 0; i < count; i++) | |
775 | { | |
776 | if (strcaseeq(map[i].str, buf)) | |
777 | { | |
778 | *out = map[i].d; | |
779 | return TRUE; | |
780 | } | |
781 | } | |
782 | return FALSE; | |
783 | } | |
784 | ||
785 | /** | |
786 | * Parse a boolean | |
787 | */ | |
788 | CALLBACK(parse_bool, bool, | |
789 | bool *out, chunk_t v) | |
790 | { | |
791 | enum_map_t map[] = { | |
792 | { "yes", TRUE }, | |
793 | { "true", TRUE }, | |
794 | { "enabled", TRUE }, | |
795 | { "1", TRUE }, | |
796 | { "no", FALSE }, | |
797 | { "false", FALSE }, | |
798 | { "disabled", FALSE }, | |
799 | { "0", FALSE }, | |
800 | }; | |
801 | int d; | |
802 | ||
803 | if (parse_map(map, countof(map), &d, v)) | |
804 | { | |
805 | *out = d; | |
806 | return TRUE; | |
807 | } | |
808 | return FALSE; | |
809 | } | |
810 | ||
811 | /** | |
812 | * Parse a ipsec_mode_t | |
813 | */ | |
814 | CALLBACK(parse_mode, bool, | |
3bedf10b | 815 | child_cfg_create_t *cfg, chunk_t v) |
b3d8bd8d MW |
816 | { |
817 | enum_map_t map[] = { | |
3bedf10b TB |
818 | { "tunnel", MODE_TUNNEL }, |
819 | { "transport", MODE_TRANSPORT }, | |
820 | { "transport_proxy", MODE_TRANSPORT }, | |
821 | { "beet", MODE_BEET }, | |
822 | { "drop", MODE_DROP }, | |
823 | { "pass", MODE_PASS }, | |
b3d8bd8d MW |
824 | }; |
825 | int d; | |
826 | ||
827 | if (parse_map(map, countof(map), &d, v)) | |
828 | { | |
3bedf10b TB |
829 | cfg->mode = d; |
830 | cfg->proxy_mode = (d == MODE_TRANSPORT) && (v.len > 9); | |
b3d8bd8d MW |
831 | return TRUE; |
832 | } | |
833 | return FALSE; | |
834 | } | |
835 | ||
836 | /** | |
837 | * Parse an action_t | |
838 | */ | |
839 | CALLBACK(parse_action, bool, | |
840 | action_t *out, chunk_t v) | |
841 | { | |
842 | enum_map_t map[] = { | |
843 | { "start", ACTION_RESTART }, | |
844 | { "restart", ACTION_RESTART }, | |
845 | { "route", ACTION_ROUTE }, | |
5619d406 | 846 | { "trap", ACTION_ROUTE }, |
b3d8bd8d MW |
847 | { "none", ACTION_NONE }, |
848 | { "clear", ACTION_NONE }, | |
849 | }; | |
850 | int d; | |
851 | ||
852 | if (parse_map(map, countof(map), &d, v)) | |
853 | { | |
854 | *out = d; | |
855 | return TRUE; | |
856 | } | |
857 | return FALSE; | |
858 | } | |
859 | ||
860 | /** | |
44fcc833 | 861 | * Parse a uint32_t with the given base |
b3d8bd8d | 862 | */ |
44fcc833 | 863 | static bool parse_uint32_base(uint32_t *out, chunk_t v, int base) |
b3d8bd8d MW |
864 | { |
865 | char buf[16], *end; | |
866 | u_long l; | |
867 | ||
868 | if (!vici_stringify(v, buf, sizeof(buf))) | |
869 | { | |
870 | return FALSE; | |
871 | } | |
44fcc833 | 872 | l = strtoul(buf, &end, base); |
b3d8bd8d MW |
873 | if (*end == 0) |
874 | { | |
875 | *out = l; | |
876 | return TRUE; | |
877 | } | |
878 | return FALSE; | |
879 | } | |
880 | ||
44fcc833 TB |
881 | /** |
882 | * Parse a uint32_t | |
883 | */ | |
884 | CALLBACK(parse_uint32, bool, | |
885 | uint32_t *out, chunk_t v) | |
886 | { | |
887 | return parse_uint32_base(out, v, 0); | |
888 | } | |
889 | ||
890 | /** | |
891 | * Parse a uint32_t in binary encoding | |
892 | */ | |
893 | CALLBACK(parse_uint32_bin, bool, | |
894 | uint32_t *out, chunk_t v) | |
895 | { | |
896 | return parse_uint32_base(out, v, 2); | |
897 | } | |
898 | ||
b3d8bd8d | 899 | /** |
b12c53ce | 900 | * Parse a uint64_t |
b3d8bd8d MW |
901 | */ |
902 | CALLBACK(parse_uint64, bool, | |
b12c53ce | 903 | uint64_t *out, chunk_t v) |
b3d8bd8d MW |
904 | { |
905 | char buf[16], *end; | |
906 | unsigned long long l; | |
907 | ||
908 | if (!vici_stringify(v, buf, sizeof(buf))) | |
909 | { | |
910 | return FALSE; | |
911 | } | |
912 | l = strtoull(buf, &end, 0); | |
913 | if (*end == 0) | |
914 | { | |
915 | *out = l; | |
916 | return TRUE; | |
917 | } | |
918 | return FALSE; | |
919 | } | |
920 | ||
921 | /** | |
922 | * Parse a relative time | |
923 | */ | |
924 | CALLBACK(parse_time, bool, | |
b12c53ce | 925 | uint64_t *out, chunk_t v) |
b3d8bd8d MW |
926 | { |
927 | char buf[16], *end; | |
928 | u_long l; | |
929 | ||
930 | if (!vici_stringify(v, buf, sizeof(buf))) | |
931 | { | |
932 | return FALSE; | |
933 | } | |
934 | ||
935 | l = strtoul(buf, &end, 0); | |
936 | while (*end == ' ') | |
937 | { | |
938 | end++; | |
939 | } | |
940 | switch (*end) | |
941 | { | |
942 | case 'd': | |
943 | case 'D': | |
944 | l *= 24; | |
945 | /* fall */ | |
946 | case 'h': | |
947 | case 'H': | |
948 | l *= 60; | |
949 | /* fall */ | |
950 | case 'm': | |
951 | case 'M': | |
952 | l *= 60; | |
953 | /* fall */ | |
954 | case 's': | |
955 | case 'S': | |
956 | end++; | |
957 | break; | |
958 | case '\0': | |
959 | break; | |
960 | default: | |
961 | return FALSE; | |
962 | } | |
963 | if (*end) | |
964 | { | |
965 | return FALSE; | |
966 | } | |
967 | *out = l; | |
968 | return TRUE; | |
969 | } | |
970 | ||
971 | /** | |
972 | * Parse byte volume | |
973 | */ | |
974 | CALLBACK(parse_bytes, bool, | |
b12c53ce | 975 | uint64_t *out, chunk_t v) |
b3d8bd8d MW |
976 | { |
977 | char buf[16], *end; | |
978 | unsigned long long l; | |
979 | ||
980 | if (!vici_stringify(v, buf, sizeof(buf))) | |
981 | { | |
982 | return FALSE; | |
983 | } | |
984 | ||
985 | l = strtoull(buf, &end, 0); | |
986 | while (*end == ' ') | |
987 | { | |
988 | end++; | |
989 | } | |
990 | switch (*end) | |
991 | { | |
992 | case 'g': | |
993 | case 'G': | |
994 | l *= 1024; | |
995 | /* fall */ | |
996 | case 'm': | |
997 | case 'M': | |
998 | l *= 1024; | |
999 | /* fall */ | |
1000 | case 'k': | |
1001 | case 'K': | |
1002 | l *= 1024; | |
1003 | end++; | |
1004 | break; | |
1005 | case '\0': | |
1006 | break; | |
1007 | default: | |
1008 | return FALSE; | |
1009 | } | |
1010 | if (*end) | |
1011 | { | |
1012 | return FALSE; | |
1013 | } | |
1014 | *out = l; | |
1015 | return TRUE; | |
1016 | } | |
1017 | ||
1018 | /** | |
1019 | * Parse a mark_t | |
1020 | */ | |
1021 | CALLBACK(parse_mark, bool, | |
1022 | mark_t *out, chunk_t v) | |
1023 | { | |
1024 | char buf[32]; | |
1025 | ||
1026 | if (!vici_stringify(v, buf, sizeof(buf))) | |
1027 | { | |
1028 | return FALSE; | |
1029 | } | |
1030 | return mark_from_string(buf, out); | |
1031 | } | |
1032 | ||
dffd6008 MW |
1033 | /** |
1034 | * Parse TFC padding option | |
1035 | */ | |
1036 | CALLBACK(parse_tfc, bool, | |
b12c53ce | 1037 | uint32_t *out, chunk_t v) |
dffd6008 MW |
1038 | { |
1039 | if (chunk_equals(v, chunk_from_str("mtu"))) | |
1040 | { | |
1041 | *out = -1; | |
1042 | return TRUE; | |
1043 | } | |
1044 | return parse_uint32(out, v); | |
1045 | } | |
1046 | ||
44fcc833 TB |
1047 | /** |
1048 | * Parse 6-bit DSCP value | |
1049 | */ | |
1050 | CALLBACK(parse_dscp, bool, | |
1051 | uint8_t *out, chunk_t v) | |
1052 | { | |
1053 | if (parse_uint32_bin(out, v)) | |
1054 | { | |
1055 | *out = *out & 0x3f; | |
1056 | return TRUE; | |
1057 | } | |
1058 | return FALSE; | |
1059 | } | |
1060 | ||
b3d8bd8d MW |
1061 | /** |
1062 | * Parse authentication config | |
1063 | */ | |
1064 | CALLBACK(parse_auth, bool, | |
1065 | auth_cfg_t *cfg, chunk_t v) | |
1066 | { | |
1067 | char buf[64], *pos; | |
1068 | eap_vendor_type_t *type; | |
1069 | ||
1070 | if (!vici_stringify(v, buf, sizeof(buf))) | |
1071 | { | |
1072 | return FALSE; | |
1073 | } | |
3c23a751 TB |
1074 | if (strpfx(buf, "ike:") || |
1075 | strpfx(buf, "pubkey") || | |
cc874350 AS |
1076 | strpfx(buf, "rsa") || |
1077 | strpfx(buf, "ecdsa") || | |
1078 | strpfx(buf, "bliss")) | |
b3d8bd8d MW |
1079 | { |
1080 | cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); | |
3c23a751 | 1081 | cfg->add_pubkey_constraints(cfg, buf, TRUE); |
b3d8bd8d MW |
1082 | return TRUE; |
1083 | } | |
1084 | if (strcaseeq(buf, "psk")) | |
1085 | { | |
1086 | cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); | |
1087 | return TRUE; | |
1088 | } | |
1089 | if (strcasepfx(buf, "xauth")) | |
1090 | { | |
1091 | pos = strchr(buf, '-'); | |
1092 | if (pos) | |
1093 | { | |
1094 | cfg->add(cfg, AUTH_RULE_XAUTH_BACKEND, strdup(++pos)); | |
1095 | } | |
1096 | cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_XAUTH); | |
1097 | return TRUE; | |
1098 | } | |
1099 | if (strcasepfx(buf, "eap")) | |
1100 | { | |
1ecec95d TB |
1101 | char *pos; |
1102 | ||
b3d8bd8d MW |
1103 | cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); |
1104 | ||
1ecec95d TB |
1105 | pos = strchr(buf, ':'); |
1106 | if (pos) | |
1107 | { | |
1108 | *pos = 0; | |
1109 | cfg->add_pubkey_constraints(cfg, pos + 1, FALSE); | |
1110 | } | |
b3d8bd8d MW |
1111 | type = eap_vendor_type_from_string(buf); |
1112 | if (type) | |
1113 | { | |
1114 | cfg->add(cfg, AUTH_RULE_EAP_TYPE, type->type); | |
1115 | if (type->vendor) | |
1116 | { | |
1117 | cfg->add(cfg, AUTH_RULE_EAP_VENDOR, type->vendor); | |
1118 | } | |
1119 | free(type); | |
1120 | } | |
1121 | return TRUE; | |
1122 | } | |
1123 | return FALSE; | |
1124 | } | |
1125 | ||
1126 | /** | |
1127 | * Parse identity; add as auth rule to config | |
1128 | */ | |
1129 | static bool parse_id(auth_cfg_t *cfg, auth_rule_t rule, chunk_t v) | |
1130 | { | |
7f65a8c2 | 1131 | char buf[BUF_LEN]; |
b3d8bd8d MW |
1132 | |
1133 | if (!vici_stringify(v, buf, sizeof(buf))) | |
1134 | { | |
1135 | return FALSE; | |
1136 | } | |
1137 | cfg->add(cfg, rule, identification_create_from_string(buf)); | |
1138 | return TRUE; | |
1139 | } | |
1140 | ||
1141 | /** | |
1142 | * Parse IKE identity | |
1143 | */ | |
1144 | CALLBACK(parse_ike_id, bool, | |
1145 | auth_cfg_t *cfg, chunk_t v) | |
1146 | { | |
1147 | return parse_id(cfg, AUTH_RULE_IDENTITY, v); | |
1148 | } | |
1149 | ||
1150 | /** | |
1151 | * Parse AAA identity | |
1152 | */ | |
1153 | CALLBACK(parse_aaa_id, bool, | |
1154 | auth_cfg_t *cfg, chunk_t v) | |
1155 | { | |
1156 | return parse_id(cfg, AUTH_RULE_AAA_IDENTITY, v); | |
1157 | } | |
1158 | ||
1159 | /** | |
1160 | * Parse EAP identity | |
1161 | */ | |
1162 | CALLBACK(parse_eap_id, bool, | |
1163 | auth_cfg_t *cfg, chunk_t v) | |
1164 | { | |
1165 | return parse_id(cfg, AUTH_RULE_EAP_IDENTITY, v); | |
1166 | } | |
1167 | ||
1168 | /** | |
1169 | * Parse XAuth identity | |
1170 | */ | |
1171 | CALLBACK(parse_xauth_id, bool, | |
1172 | auth_cfg_t *cfg, chunk_t v) | |
1173 | { | |
1174 | return parse_id(cfg, AUTH_RULE_XAUTH_IDENTITY, v); | |
1175 | } | |
1176 | ||
e6e975ff MW |
1177 | /** |
1178 | * Parse group membership | |
1179 | */ | |
1180 | CALLBACK(parse_group, bool, | |
1181 | auth_cfg_t *cfg, chunk_t v) | |
1182 | { | |
1183 | return parse_id(cfg, AUTH_RULE_GROUP, v); | |
1184 | } | |
1185 | ||
e00bc9f6 TB |
1186 | /** |
1187 | * Parse certificate policy | |
1188 | */ | |
1189 | CALLBACK(parse_cert_policy, bool, | |
1190 | auth_cfg_t *cfg, chunk_t v) | |
1191 | { | |
1192 | char buf[BUF_LEN]; | |
1193 | ||
1194 | if (!vici_stringify(v, buf, sizeof(buf))) | |
1195 | { | |
1196 | return FALSE; | |
1197 | } | |
1198 | cfg->add(cfg, AUTH_RULE_CERT_POLICY, strdup(buf)); | |
1199 | return TRUE; | |
1200 | } | |
1201 | ||
b57739f7 | 1202 | /** |
00bf6a2a | 1203 | * Add a certificate as auth rule to config |
b57739f7 | 1204 | */ |
00bf6a2a | 1205 | static bool add_cert(auth_data_t *auth, auth_rule_t rule, certificate_t *cert) |
b57739f7 | 1206 | { |
63d37038 | 1207 | vici_authority_t *authority; |
87371460 | 1208 | vici_cred_t *cred; |
00bf6a2a TB |
1209 | |
1210 | if (rule == AUTH_RULE_SUBJECT_CERT) | |
1211 | { | |
1212 | authority = auth->request->this->authority; | |
1213 | authority->check_for_hash_and_url(authority, cert); | |
1214 | } | |
1215 | cred = auth->request->this->cred; | |
1216 | cert = cred->add_cert(cred, cert); | |
1217 | auth->cfg->add(auth->cfg, rule, cert); | |
1218 | return TRUE; | |
1219 | } | |
1220 | ||
1221 | /** | |
1222 | * Parse a certificate; add as auth rule to config | |
1223 | */ | |
1224 | static bool parse_cert(auth_data_t *auth, auth_rule_t rule, chunk_t v) | |
1225 | { | |
b57739f7 MW |
1226 | certificate_t *cert; |
1227 | ||
1228 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, | |
1229 | BUILD_BLOB_PEM, v, BUILD_END); | |
1230 | if (cert) | |
1231 | { | |
00bf6a2a | 1232 | return add_cert(auth, rule, cert); |
b57739f7 MW |
1233 | } |
1234 | return FALSE; | |
1235 | } | |
1236 | ||
1237 | /** | |
1238 | * Parse subject certificates | |
1239 | */ | |
1240 | CALLBACK(parse_certs, bool, | |
63d37038 | 1241 | auth_data_t *auth, chunk_t v) |
b57739f7 | 1242 | { |
63d37038 | 1243 | return parse_cert(auth, AUTH_RULE_SUBJECT_CERT, v); |
b57739f7 MW |
1244 | } |
1245 | ||
1246 | /** | |
1247 | * Parse CA certificates | |
1248 | */ | |
1249 | CALLBACK(parse_cacerts, bool, | |
63d37038 | 1250 | auth_data_t *auth, chunk_t v) |
b57739f7 | 1251 | { |
63d37038 | 1252 | return parse_cert(auth, AUTH_RULE_CA_CERT, v); |
b57739f7 MW |
1253 | } |
1254 | ||
87371460 AS |
1255 | /** |
1256 | * Parse raw public keys | |
1257 | */ | |
1258 | CALLBACK(parse_pubkeys, bool, | |
1259 | auth_data_t *auth, chunk_t v) | |
1260 | { | |
1261 | vici_cred_t *cred; | |
1262 | certificate_t *cert; | |
1263 | ||
1264 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY, | |
1265 | BUILD_BLOB_PEM, v, BUILD_END); | |
1266 | if (cert) | |
1267 | { | |
1268 | cred = auth->request->this->cred; | |
1269 | cert = cred->add_cert(cred, cert); | |
1270 | auth->cfg->add(auth->cfg, AUTH_RULE_SUBJECT_CERT, cert); | |
1271 | return TRUE; | |
1272 | } | |
1273 | return FALSE; | |
1274 | } | |
1275 | ||
b3d8bd8d MW |
1276 | /** |
1277 | * Parse revocation status | |
1278 | */ | |
1279 | CALLBACK(parse_revocation, bool, | |
1280 | auth_cfg_t *cfg, chunk_t v) | |
1281 | { | |
1282 | enum_map_t map[] = { | |
1283 | { "strict", VALIDATION_GOOD }, | |
1284 | { "ifuri", VALIDATION_SKIPPED }, | |
f3e1ec4a | 1285 | { "relaxed", VALIDATION_FAILED }, |
b3d8bd8d MW |
1286 | }; |
1287 | int d; | |
1288 | ||
1289 | if (parse_map(map, countof(map), &d, v)) | |
1290 | { | |
f3e1ec4a MW |
1291 | if (d != VALIDATION_FAILED) |
1292 | { | |
1293 | cfg->add(cfg, AUTH_RULE_CRL_VALIDATION, d); | |
1294 | } | |
b3d8bd8d MW |
1295 | return TRUE; |
1296 | } | |
1297 | return FALSE; | |
1298 | } | |
1299 | ||
1300 | /** | |
1301 | * Parse list items to comma separated strings | |
1302 | */ | |
1303 | CALLBACK(parse_stringlist, bool, | |
1304 | char **out, chunk_t v) | |
1305 | { | |
1306 | char *current; | |
1307 | ||
1308 | if (!chunk_printable(v, NULL, ' ')) | |
1309 | { | |
1310 | return FALSE; | |
1311 | } | |
1312 | current = *out; | |
1313 | if (current) | |
1314 | { | |
1315 | if (asprintf(out, "%s, %.*s", current, (int)v.len, v.ptr) == -1) | |
1316 | { | |
1317 | return FALSE; | |
1318 | } | |
1319 | free(current); | |
1320 | } | |
1321 | else | |
1322 | { | |
1323 | if (asprintf(out, "%.*s", (int)v.len, v.ptr) == -1) | |
1324 | { | |
1325 | return FALSE; | |
1326 | } | |
1327 | } | |
1328 | return TRUE; | |
1329 | } | |
1330 | ||
1331 | /** | |
1332 | * Parse an fragmentation_t | |
1333 | */ | |
1334 | CALLBACK(parse_frag, bool, | |
1335 | fragmentation_t *out, chunk_t v) | |
1336 | { | |
1337 | enum_map_t map[] = { | |
1338 | { "yes", FRAGMENTATION_YES }, | |
46a3f92a | 1339 | { "accept", FRAGMENTATION_ACCEPT }, |
b3d8bd8d MW |
1340 | { "no", FRAGMENTATION_NO }, |
1341 | { "force", FRAGMENTATION_FORCE }, | |
1342 | }; | |
1343 | int d; | |
1344 | ||
1345 | if (parse_map(map, countof(map), &d, v)) | |
1346 | { | |
1347 | *out = d; | |
1348 | return TRUE; | |
1349 | } | |
1350 | return FALSE; | |
1351 | } | |
1352 | ||
1353 | /** | |
1354 | * Parse a cert_policy_t | |
1355 | */ | |
1356 | CALLBACK(parse_send_cert, bool, | |
1357 | cert_policy_t *out, chunk_t v) | |
1358 | { | |
1359 | enum_map_t map[] = { | |
1360 | { "ifasked", CERT_SEND_IF_ASKED }, | |
1361 | { "always", CERT_ALWAYS_SEND }, | |
1362 | { "never", CERT_NEVER_SEND }, | |
1363 | }; | |
1364 | int d; | |
1365 | ||
1366 | if (parse_map(map, countof(map), &d, v)) | |
1367 | { | |
1368 | *out = d; | |
1369 | return TRUE; | |
1370 | } | |
1371 | return FALSE; | |
1372 | } | |
1373 | ||
1374 | /** | |
1375 | * Parse a unique_policy_t | |
1376 | */ | |
1377 | CALLBACK(parse_unique, bool, | |
1378 | unique_policy_t *out, chunk_t v) | |
1379 | { | |
1380 | enum_map_t map[] = { | |
1381 | { "never", UNIQUE_NEVER }, | |
1382 | { "no", UNIQUE_NO }, | |
1383 | { "replace", UNIQUE_REPLACE }, | |
1384 | { "keep", UNIQUE_KEEP }, | |
1385 | }; | |
1386 | int d; | |
1387 | ||
1388 | if (parse_map(map, countof(map), &d, v)) | |
1389 | { | |
1390 | *out = d; | |
1391 | return TRUE; | |
1392 | } | |
1393 | return FALSE; | |
1394 | } | |
1395 | ||
1396 | /** | |
1397 | * Parse host_t into a list | |
1398 | */ | |
1399 | CALLBACK(parse_hosts, bool, | |
1400 | linked_list_t *list, chunk_t v) | |
1401 | { | |
1402 | char buf[64]; | |
1403 | host_t *host; | |
1404 | ||
1405 | if (!vici_stringify(v, buf, sizeof(buf))) | |
1406 | { | |
1407 | return FALSE; | |
1408 | } | |
1409 | host = host_create_from_string(buf, 0); | |
1410 | if (!host) | |
1411 | { | |
1412 | return FALSE; | |
1413 | } | |
1414 | list->insert_last(list, host); | |
1415 | return TRUE; | |
1416 | } | |
1417 | ||
f927ba97 TB |
1418 | #ifdef ME |
1419 | /** | |
1420 | * Parse peer ID | |
1421 | */ | |
1422 | CALLBACK(parse_peer_id, bool, | |
1423 | identification_t **out, chunk_t v) | |
1424 | { | |
1425 | char buf[BUF_LEN]; | |
1426 | ||
1427 | if (!vici_stringify(v, buf, sizeof(buf))) | |
1428 | { | |
1429 | return FALSE; | |
1430 | } | |
1431 | *out = identification_create_from_string(buf); | |
1432 | return TRUE; | |
1433 | } | |
1434 | #endif /* ME */ | |
1435 | ||
00bf6a2a TB |
1436 | CALLBACK(cert_kv, bool, |
1437 | cert_data_t *cert, vici_message_t *message, char *name, chunk_t value) | |
1438 | { | |
1439 | parse_rule_t rules[] = { | |
1440 | { "handle", parse_string, &cert->handle }, | |
1441 | { "slot", parse_uint32, &cert->slot }, | |
1442 | { "module", parse_string, &cert->module }, | |
2f8354ca | 1443 | { "file", parse_string, &cert->file }, |
00bf6a2a TB |
1444 | }; |
1445 | ||
1446 | return parse_rules(rules, countof(rules), name, value, | |
1447 | &cert->request->reply); | |
1448 | } | |
1449 | ||
b3d8bd8d MW |
1450 | CALLBACK(child_li, bool, |
1451 | child_data_t *child, vici_message_t *message, char *name, chunk_t value) | |
1452 | { | |
1453 | parse_rule_t rules[] = { | |
1454 | { "ah_proposals", parse_ah_proposal, child->proposals }, | |
1455 | { "esp_proposals", parse_esp_proposal, child->proposals }, | |
1456 | { "local_ts", parse_ts, child->local_ts }, | |
1457 | { "remote_ts", parse_ts, child->remote_ts }, | |
1458 | }; | |
1459 | ||
1460 | return parse_rules(rules, countof(rules), name, value, | |
1461 | &child->request->reply); | |
1462 | } | |
1463 | ||
1464 | CALLBACK(child_kv, bool, | |
1465 | child_data_t *child, vici_message_t *message, char *name, chunk_t value) | |
1466 | { | |
1467 | parse_rule_t rules[] = { | |
50721a61 TB |
1468 | { "updown", parse_string, &child->cfg.updown }, |
1469 | { "hostaccess", parse_bool, &child->cfg.hostaccess }, | |
3bedf10b | 1470 | { "mode", parse_mode, &child->cfg }, |
50721a61 TB |
1471 | { "policies", parse_bool, &child->policies }, |
1472 | { "policies_fwd_out", parse_bool, &child->policies_fwd_out }, | |
1473 | { "replay_window", parse_uint32, &child->replay_window }, | |
1474 | { "rekey_time", parse_time, &child->cfg.lifetime.time.rekey }, | |
1475 | { "life_time", parse_time, &child->cfg.lifetime.time.life }, | |
1476 | { "rand_time", parse_time, &child->cfg.lifetime.time.jitter }, | |
1477 | { "rekey_bytes", parse_bytes, &child->cfg.lifetime.bytes.rekey }, | |
1478 | { "life_bytes", parse_bytes, &child->cfg.lifetime.bytes.life }, | |
1479 | { "rand_bytes", parse_bytes, &child->cfg.lifetime.bytes.jitter }, | |
1480 | { "rekey_packets", parse_uint64, &child->cfg.lifetime.packets.rekey }, | |
1481 | { "life_packets", parse_uint64, &child->cfg.lifetime.packets.life }, | |
1482 | { "rand_packets", parse_uint64, &child->cfg.lifetime.packets.jitter }, | |
1483 | { "dpd_action", parse_action, &child->cfg.dpd_action }, | |
1484 | { "start_action", parse_action, &child->cfg.start_action }, | |
1485 | { "close_action", parse_action, &child->cfg.close_action }, | |
1486 | { "ipcomp", parse_bool, &child->cfg.ipcomp }, | |
1487 | { "inactivity", parse_time, &child->cfg.inactivity }, | |
1488 | { "reqid", parse_uint32, &child->cfg.reqid }, | |
1489 | { "mark_in", parse_mark, &child->cfg.mark_in }, | |
1490 | { "mark_out", parse_mark, &child->cfg.mark_out }, | |
1491 | { "tfc_padding", parse_tfc, &child->cfg.tfc }, | |
1492 | { "priority", parse_uint32, &child->cfg.priority }, | |
1493 | { "interface", parse_string, &child->cfg.interface }, | |
b3d8bd8d MW |
1494 | }; |
1495 | ||
1496 | return parse_rules(rules, countof(rules), name, value, | |
1497 | &child->request->reply); | |
1498 | } | |
1499 | ||
e6e975ff MW |
1500 | CALLBACK(auth_li, bool, |
1501 | auth_data_t *auth, vici_message_t *message, char *name, chunk_t value) | |
1502 | { | |
1503 | parse_rule_t rules[] = { | |
1504 | { "groups", parse_group, auth->cfg }, | |
e00bc9f6 | 1505 | { "cert_policy", parse_cert_policy, auth }, |
63d37038 AS |
1506 | { "certs", parse_certs, auth }, |
1507 | { "cacerts", parse_cacerts, auth }, | |
87371460 | 1508 | { "pubkeys", parse_pubkeys, auth }, |
e6e975ff MW |
1509 | }; |
1510 | ||
1511 | return parse_rules(rules, countof(rules), name, value, | |
1512 | &auth->request->reply); | |
1513 | } | |
1514 | ||
b3d8bd8d MW |
1515 | CALLBACK(auth_kv, bool, |
1516 | auth_data_t *auth, vici_message_t *message, char *name, chunk_t value) | |
1517 | { | |
1518 | parse_rule_t rules[] = { | |
1519 | { "auth", parse_auth, auth->cfg }, | |
1520 | { "id", parse_ike_id, auth->cfg }, | |
1521 | { "aaa_id", parse_aaa_id, auth->cfg }, | |
1522 | { "eap_id", parse_eap_id, auth->cfg }, | |
1523 | { "xauth_id", parse_xauth_id, auth->cfg }, | |
1524 | { "revocation", parse_revocation, auth->cfg }, | |
229cdf6b | 1525 | { "round", parse_uint32, &auth->round }, |
b3d8bd8d MW |
1526 | }; |
1527 | ||
1528 | return parse_rules(rules, countof(rules), name, value, | |
1529 | &auth->request->reply); | |
1530 | } | |
1531 | ||
1532 | CALLBACK(peer_li, bool, | |
1533 | peer_data_t *peer, vici_message_t *message, char *name, chunk_t value) | |
1534 | { | |
1535 | parse_rule_t rules[] = { | |
1536 | { "local_addrs", parse_stringlist, &peer->local_addrs }, | |
1537 | { "remote_addrs", parse_stringlist, &peer->remote_addrs }, | |
1538 | { "proposals", parse_ike_proposal, peer->proposals }, | |
1539 | { "vips", parse_hosts, peer->vips }, | |
afb8f492 | 1540 | { "pools", parse_stringlist, &peer->pools }, |
b3d8bd8d MW |
1541 | }; |
1542 | ||
1543 | return parse_rules(rules, countof(rules), name, value, | |
1544 | &peer->request->reply); | |
1545 | } | |
1546 | ||
1547 | CALLBACK(peer_kv, bool, | |
1548 | peer_data_t *peer, vici_message_t *message, char *name, chunk_t value) | |
1549 | { | |
1550 | parse_rule_t rules[] = { | |
1551 | { "version", parse_uint32, &peer->version }, | |
1552 | { "aggressive", parse_bool, &peer->aggressive }, | |
1553 | { "pull", parse_bool, &peer->pull }, | |
44fcc833 | 1554 | { "dscp", parse_dscp, &peer->dscp }, |
b3d8bd8d MW |
1555 | { "encap", parse_bool, &peer->encap }, |
1556 | { "mobike", parse_bool, &peer->mobike }, | |
1557 | { "dpd_delay", parse_time, &peer->dpd_delay }, | |
1558 | { "dpd_timeout", parse_time, &peer->dpd_timeout }, | |
1559 | { "fragmentation", parse_frag, &peer->fragmentation }, | |
1560 | { "send_certreq", parse_bool, &peer->send_certreq }, | |
1561 | { "send_cert", parse_send_cert, &peer->send_cert }, | |
1562 | { "keyingtries", parse_uint32, &peer->keyingtries }, | |
1563 | { "unique", parse_unique, &peer->unique }, | |
1564 | { "local_port", parse_uint32, &peer->local_port }, | |
1565 | { "remote_port", parse_uint32, &peer->remote_port }, | |
1566 | { "reauth_time", parse_time, &peer->reauth_time }, | |
1567 | { "rekey_time", parse_time, &peer->rekey_time }, | |
1568 | { "over_time", parse_time, &peer->over_time }, | |
1569 | { "rand_time", parse_time, &peer->rand_time }, | |
f927ba97 TB |
1570 | #ifdef ME |
1571 | { "mediation", parse_bool, &peer->mediation }, | |
1572 | { "mediated_by", parse_string, &peer->mediated_by }, | |
1573 | { "mediation_peer", parse_peer_id, &peer->peer_id }, | |
1574 | #endif /* ME */ | |
b3d8bd8d MW |
1575 | }; |
1576 | ||
1577 | return parse_rules(rules, countof(rules), name, value, | |
1578 | &peer->request->reply); | |
1579 | } | |
1580 | ||
00bf6a2a TB |
1581 | CALLBACK(auth_sn, bool, |
1582 | auth_data_t *auth, vici_message_t *message, vici_parse_context_t *ctx, | |
1583 | char *name) | |
1584 | { | |
1585 | if (strcasepfx(name, "cert") || | |
1586 | strcasepfx(name, "cacert")) | |
1587 | { | |
1588 | cert_data_t *data; | |
1589 | auth_rule_t rule; | |
1590 | certificate_t *cert; | |
1591 | chunk_t handle; | |
1592 | ||
1593 | INIT(data, | |
1594 | .request = auth->request, | |
1595 | .slot = -1, | |
1596 | ); | |
1597 | ||
1598 | if (!message->parse(message, ctx, NULL, cert_kv, NULL, data)) | |
1599 | { | |
1600 | free_cert_data(data); | |
1601 | return FALSE; | |
1602 | } | |
2f8354ca | 1603 | if (!data->handle && !data->file) |
00bf6a2a | 1604 | { |
2f8354ca TB |
1605 | auth->request->reply = create_reply("handle or file path missing: " |
1606 | "%s", name); | |
1607 | free_cert_data(data); | |
1608 | return FALSE; | |
1609 | } | |
1610 | else if (data->handle && data->file) | |
1611 | { | |
1612 | auth->request->reply = create_reply("handle and file path given: " | |
1613 | "%s", name); | |
00bf6a2a TB |
1614 | free_cert_data(data); |
1615 | return FALSE; | |
1616 | } | |
1617 | ||
2f8354ca | 1618 | if (data->file) |
00bf6a2a TB |
1619 | { |
1620 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, | |
2f8354ca | 1621 | BUILD_FROM_FILE, data->file, BUILD_END); |
00bf6a2a TB |
1622 | } |
1623 | else | |
1624 | { | |
2f8354ca TB |
1625 | handle = chunk_from_hex(chunk_from_str(data->handle), NULL); |
1626 | if (data->slot != -1) | |
1627 | { | |
1628 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, | |
1629 | CERT_X509, BUILD_PKCS11_KEYID, handle, | |
1630 | BUILD_PKCS11_SLOT, data->slot, | |
1631 | data->module ? BUILD_PKCS11_MODULE : BUILD_END, | |
1632 | data->module, BUILD_END); | |
1633 | } | |
1634 | else | |
1635 | { | |
1636 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, | |
1637 | CERT_X509, BUILD_PKCS11_KEYID, handle, | |
1638 | data->module ? BUILD_PKCS11_MODULE : BUILD_END, | |
1639 | data->module, BUILD_END); | |
1640 | } | |
1641 | chunk_free(&handle); | |
00bf6a2a | 1642 | } |
00bf6a2a TB |
1643 | free_cert_data(data); |
1644 | if (!cert) | |
1645 | { | |
1646 | auth->request->reply = create_reply("unable to load certificate: " | |
1647 | "%s", name); | |
1648 | return FALSE; | |
1649 | } | |
1650 | rule = strcasepfx(name, "cert") ? AUTH_RULE_SUBJECT_CERT | |
1651 | : AUTH_RULE_CA_CERT; | |
1652 | return add_cert(auth, rule, cert); | |
1653 | } | |
1654 | auth->request->reply = create_reply("invalid section: %s", name); | |
1655 | return FALSE; | |
1656 | } | |
1657 | ||
8a00a845 TB |
1658 | /** |
1659 | * Check and update lifetimes | |
1660 | */ | |
1661 | static void check_lifetimes(lifetime_cfg_t *lft) | |
1662 | { | |
1663 | /* if no hard lifetime specified, add one at soft lifetime + 10% */ | |
1664 | if (lft->time.life == LFT_UNDEFINED) | |
1665 | { | |
1666 | lft->time.life = lft->time.rekey * 110 / 100; | |
1667 | } | |
1668 | if (lft->bytes.life == LFT_UNDEFINED) | |
1669 | { | |
1670 | lft->bytes.life = lft->bytes.rekey * 110 / 100; | |
1671 | } | |
1672 | if (lft->packets.life == LFT_UNDEFINED) | |
1673 | { | |
1674 | lft->packets.life = lft->packets.rekey * 110 / 100; | |
1675 | } | |
8a00a845 TB |
1676 | /* if no rand time defined, use difference of hard and soft */ |
1677 | if (lft->time.jitter == LFT_UNDEFINED) | |
1678 | { | |
1679 | lft->time.jitter = lft->time.life - | |
1680 | min(lft->time.life, lft->time.rekey); | |
1681 | } | |
1682 | if (lft->bytes.jitter == LFT_UNDEFINED) | |
1683 | { | |
1684 | lft->bytes.jitter = lft->bytes.life - | |
1685 | min(lft->bytes.life, lft->bytes.rekey); | |
1686 | } | |
1687 | if (lft->packets.jitter == LFT_UNDEFINED) | |
1688 | { | |
1689 | lft->packets.jitter = lft->packets.life - | |
1690 | min(lft->packets.life, lft->packets.rekey); | |
1691 | } | |
1692 | } | |
1693 | ||
b3d8bd8d MW |
1694 | CALLBACK(children_sn, bool, |
1695 | peer_data_t *peer, vici_message_t *message, vici_parse_context_t *ctx, | |
1696 | char *name) | |
1697 | { | |
1698 | child_data_t child = { | |
1699 | .request = peer->request, | |
1700 | .proposals = linked_list_create(), | |
1701 | .local_ts = linked_list_create(), | |
1702 | .remote_ts = linked_list_create(), | |
9322e5b3 | 1703 | .policies = TRUE, |
d73a4617 | 1704 | .replay_window = REPLAY_UNDEFINED, |
8a00a845 TB |
1705 | .cfg = { |
1706 | .mode = MODE_TUNNEL, | |
1707 | .lifetime = { | |
1708 | .time = { | |
b1df6312 | 1709 | .rekey = LFT_DEFAULT_CHILD_REKEY_TIME, |
8a00a845 TB |
1710 | .life = LFT_UNDEFINED, |
1711 | .jitter = LFT_UNDEFINED, | |
1712 | }, | |
1713 | .bytes = { | |
b1df6312 | 1714 | .rekey = LFT_DEFAULT_CHILD_REKEY_BYTES, |
8a00a845 TB |
1715 | .life = LFT_UNDEFINED, |
1716 | .jitter = LFT_UNDEFINED, | |
1717 | }, | |
1718 | .packets = { | |
b1df6312 | 1719 | .rekey = LFT_DEFAULT_CHILD_REKEY_PACKETS, |
8a00a845 TB |
1720 | .life = LFT_UNDEFINED, |
1721 | .jitter = LFT_UNDEFINED, | |
1722 | }, | |
c5205105 | 1723 | }, |
8a00a845 | 1724 | }, |
b3d8bd8d MW |
1725 | }; |
1726 | child_cfg_t *cfg; | |
1727 | proposal_t *proposal; | |
1728 | traffic_selector_t *ts; | |
1729 | ||
1730 | if (!message->parse(message, ctx, NULL, child_kv, child_li, &child)) | |
1731 | { | |
1732 | free_child_data(&child); | |
1733 | return FALSE; | |
1734 | } | |
1735 | ||
1736 | if (child.local_ts->get_count(child.local_ts) == 0) | |
1737 | { | |
1738 | child.local_ts->insert_last(child.local_ts, | |
1739 | traffic_selector_create_dynamic(0, 0, 65535)); | |
1740 | } | |
1741 | if (child.remote_ts->get_count(child.remote_ts) == 0) | |
1742 | { | |
1743 | child.remote_ts->insert_last(child.remote_ts, | |
1744 | traffic_selector_create_dynamic(0, 0, 65535)); | |
1745 | } | |
1746 | if (child.proposals->get_count(child.proposals) == 0) | |
1747 | { | |
8d74ec9e MW |
1748 | proposal = proposal_create_default(PROTO_ESP); |
1749 | if (proposal) | |
1750 | { | |
1751 | child.proposals->insert_last(child.proposals, proposal); | |
1752 | } | |
1753 | proposal = proposal_create_default_aead(PROTO_ESP); | |
1754 | if (proposal) | |
1755 | { | |
1756 | child.proposals->insert_last(child.proposals, proposal); | |
1757 | } | |
b3d8bd8d | 1758 | } |
8a00a845 | 1759 | child.cfg.suppress_policies = !child.policies; |
50721a61 | 1760 | child.cfg.fwd_out_policies = child.policies_fwd_out; |
b3d8bd8d | 1761 | |
8a00a845 | 1762 | check_lifetimes(&child.cfg.lifetime); |
c5205105 | 1763 | |
b3d8bd8d MW |
1764 | log_child_data(&child, name); |
1765 | ||
8a00a845 | 1766 | cfg = child_cfg_create(name, &child.cfg); |
9322e5b3 | 1767 | |
d73a4617 MW |
1768 | if (child.replay_window != REPLAY_UNDEFINED) |
1769 | { | |
1770 | cfg->set_replay_window(cfg, child.replay_window); | |
1771 | } | |
b3d8bd8d MW |
1772 | while (child.local_ts->remove_first(child.local_ts, |
1773 | (void**)&ts) == SUCCESS) | |
1774 | { | |
1775 | cfg->add_traffic_selector(cfg, TRUE, ts); | |
1776 | } | |
1777 | while (child.remote_ts->remove_first(child.remote_ts, | |
1778 | (void**)&ts) == SUCCESS) | |
1779 | { | |
1780 | cfg->add_traffic_selector(cfg, FALSE, ts); | |
1781 | } | |
1782 | while (child.proposals->remove_first(child.proposals, | |
1783 | (void**)&proposal) == SUCCESS) | |
1784 | { | |
1785 | cfg->add_proposal(cfg, proposal); | |
1786 | } | |
1787 | ||
1788 | peer->children->insert_last(peer->children, cfg); | |
1789 | ||
1790 | free_child_data(&child); | |
1791 | ||
1792 | return TRUE; | |
1793 | } | |
1794 | ||
1795 | CALLBACK(peer_sn, bool, | |
1796 | peer_data_t *peer, vici_message_t *message, vici_parse_context_t *ctx, | |
1797 | char *name) | |
1798 | { | |
1799 | if (strcaseeq(name, "children")) | |
1800 | { | |
1801 | return message->parse(message, ctx, children_sn, NULL, NULL, peer); | |
1802 | } | |
1803 | if (strcasepfx(name, "local") || | |
1804 | strcasepfx(name, "remote")) | |
1805 | { | |
229cdf6b TB |
1806 | enumerator_t *enumerator; |
1807 | linked_list_t *auths; | |
1808 | auth_data_t *auth, *current; | |
ffd29ab3 | 1809 | auth_rule_t rule; |
87371460 | 1810 | certificate_t *cert; |
ffd29ab3 | 1811 | pubkey_cert_t *pubkey_cert; |
87371460 | 1812 | identification_t *id; |
ffd29ab3 | 1813 | bool default_id = FALSE; |
b3d8bd8d | 1814 | |
229cdf6b TB |
1815 | INIT(auth, |
1816 | .request = peer->request, | |
1817 | .cfg = auth_cfg_create(), | |
1818 | ); | |
1819 | ||
00bf6a2a | 1820 | if (!message->parse(message, ctx, auth_sn, auth_kv, auth_li, auth)) |
b3d8bd8d | 1821 | { |
229cdf6b | 1822 | free_auth_data(auth); |
b3d8bd8d MW |
1823 | return FALSE; |
1824 | } | |
229cdf6b | 1825 | id = auth->cfg->get(auth->cfg, AUTH_RULE_IDENTITY); |
b3d8bd8d | 1826 | |
229cdf6b | 1827 | enumerator = auth->cfg->create_enumerator(auth->cfg); |
ffd29ab3 | 1828 | while (enumerator->enumerate(enumerator, &rule, &cert)) |
17d34356 | 1829 | { |
ffd29ab3 | 1830 | if (rule == AUTH_RULE_SUBJECT_CERT && !default_id) |
87371460 | 1831 | { |
ffd29ab3 | 1832 | if (id == NULL) |
87371460 | 1833 | { |
ffd29ab3 AS |
1834 | id = cert->get_subject(cert); |
1835 | DBG1(DBG_CFG, " id not specified, defaulting to" | |
1836 | " cert subject '%Y'", id); | |
229cdf6b | 1837 | auth->cfg->add(auth->cfg, AUTH_RULE_IDENTITY, id->clone(id)); |
ffd29ab3 AS |
1838 | default_id = TRUE; |
1839 | } | |
1840 | else if (cert->get_type(cert) == CERT_TRUSTED_PUBKEY && | |
1841 | id->get_type != ID_ANY) | |
1842 | { | |
1843 | /* set the subject of all raw public keys to the id */ | |
87371460 AS |
1844 | pubkey_cert = (pubkey_cert_t*)cert; |
1845 | pubkey_cert->set_subject(pubkey_cert, id); | |
1846 | } | |
1847 | } | |
17d34356 | 1848 | } |
ffd29ab3 | 1849 | enumerator->destroy(enumerator); |
17d34356 | 1850 | |
229cdf6b TB |
1851 | auths = strcasepfx(name, "local") ? peer->local : peer->remote; |
1852 | enumerator = auths->create_enumerator(auths); | |
1853 | while (enumerator->enumerate(enumerator, ¤t)) | |
b3d8bd8d | 1854 | { |
229cdf6b TB |
1855 | if (auth->round < current->round) |
1856 | { | |
1857 | break; | |
1858 | } | |
b3d8bd8d | 1859 | } |
229cdf6b TB |
1860 | auths->insert_before(auths, enumerator, auth); |
1861 | enumerator->destroy(enumerator); | |
b3d8bd8d MW |
1862 | return TRUE; |
1863 | } | |
1864 | peer->request->reply = create_reply("invalid section: %s", name); | |
1865 | return FALSE; | |
1866 | } | |
1867 | ||
7de35b7f MW |
1868 | /** |
1869 | * Find reqid of an existing CHILD_SA | |
1870 | */ | |
b12c53ce | 1871 | static uint32_t find_reqid(child_cfg_t *cfg) |
7de35b7f MW |
1872 | { |
1873 | enumerator_t *enumerator, *children; | |
1874 | child_sa_t *child_sa; | |
1875 | ike_sa_t *ike_sa; | |
b12c53ce | 1876 | uint32_t reqid; |
7de35b7f MW |
1877 | |
1878 | reqid = charon->traps->find_reqid(charon->traps, cfg); | |
1879 | if (reqid) | |
1880 | { /* already trapped */ | |
1881 | return reqid; | |
1882 | } | |
1883 | ||
1884 | enumerator = charon->controller->create_ike_sa_enumerator( | |
1885 | charon->controller, TRUE); | |
1886 | while (!reqid && enumerator->enumerate(enumerator, &ike_sa)) | |
1887 | { | |
1888 | children = ike_sa->create_child_sa_enumerator(ike_sa); | |
1889 | while (children->enumerate(children, &child_sa)) | |
1890 | { | |
1891 | if (streq(cfg->get_name(cfg), child_sa->get_name(child_sa))) | |
1892 | { | |
1893 | reqid = child_sa->get_reqid(child_sa); | |
1894 | break; | |
1895 | } | |
1896 | } | |
1897 | children->destroy(children); | |
1898 | } | |
1899 | enumerator->destroy(enumerator); | |
1900 | return reqid; | |
1901 | } | |
1902 | ||
1903 | /** | |
20df9d31 | 1904 | * Perform start actions associated with a child config |
7de35b7f MW |
1905 | */ |
1906 | static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg, | |
1907 | child_cfg_t *child_cfg) | |
1908 | { | |
1909 | switch (child_cfg->get_start_action(child_cfg)) | |
1910 | { | |
1911 | case ACTION_RESTART: | |
1912 | DBG1(DBG_CFG, "initiating '%s'", child_cfg->get_name(child_cfg)); | |
1913 | charon->controller->initiate(charon->controller, | |
1914 | peer_cfg->get_ref(peer_cfg), child_cfg->get_ref(child_cfg), | |
ff0abde9 | 1915 | NULL, NULL, 0, FALSE); |
7de35b7f MW |
1916 | break; |
1917 | case ACTION_ROUTE: | |
1918 | DBG1(DBG_CFG, "installing '%s'", child_cfg->get_name(child_cfg)); | |
1919 | switch (child_cfg->get_mode(child_cfg)) | |
1920 | { | |
1921 | case MODE_PASS: | |
1922 | case MODE_DROP: | |
7627f5f9 TB |
1923 | charon->shunts->install(charon->shunts, |
1924 | peer_cfg->get_name(peer_cfg), child_cfg); | |
7de35b7f MW |
1925 | break; |
1926 | default: | |
1927 | charon->traps->install(charon->traps, peer_cfg, child_cfg, | |
1928 | find_reqid(child_cfg)); | |
1929 | break; | |
1930 | } | |
1931 | break; | |
1932 | default: | |
1933 | break; | |
1934 | } | |
1935 | } | |
1936 | ||
1937 | /** | |
20df9d31 | 1938 | * Undo start actions associated with a child config |
7de35b7f | 1939 | */ |
b26ba1b4 | 1940 | static void clear_start_action(private_vici_config_t *this, char *peer_name, |
7de35b7f MW |
1941 | child_cfg_t *child_cfg) |
1942 | { | |
1943 | enumerator_t *enumerator, *children; | |
1944 | child_sa_t *child_sa; | |
7627f5f9 | 1945 | peer_cfg_t *peer_cfg; |
7de35b7f | 1946 | ike_sa_t *ike_sa; |
b12c53ce | 1947 | uint32_t id = 0, others; |
23b1f713 | 1948 | array_t *ids = NULL, *ikeids = NULL; |
7de35b7f MW |
1949 | char *name; |
1950 | ||
1951 | name = child_cfg->get_name(child_cfg); | |
20df9d31 | 1952 | |
7de35b7f MW |
1953 | switch (child_cfg->get_start_action(child_cfg)) |
1954 | { | |
1955 | case ACTION_RESTART: | |
1956 | enumerator = charon->controller->create_ike_sa_enumerator( | |
1957 | charon->controller, TRUE); | |
1958 | while (enumerator->enumerate(enumerator, &ike_sa)) | |
1959 | { | |
b26ba1b4 MW |
1960 | if (!streq(ike_sa->get_name(ike_sa), peer_name)) |
1961 | { | |
1962 | continue; | |
1963 | } | |
23b1f713 | 1964 | others = id = 0; |
7de35b7f MW |
1965 | children = ike_sa->create_child_sa_enumerator(ike_sa); |
1966 | while (children->enumerate(children, &child_sa)) | |
1967 | { | |
23b1f713 | 1968 | if (child_sa->get_state(child_sa) != CHILD_DELETING) |
afb7ef49 | 1969 | { |
23b1f713 MW |
1970 | if (streq(name, child_sa->get_name(child_sa))) |
1971 | { | |
1972 | id = child_sa->get_unique_id(child_sa); | |
1973 | } | |
1974 | else | |
1975 | { | |
1976 | others++; | |
1977 | } | |
afb7ef49 | 1978 | } |
7de35b7f MW |
1979 | } |
1980 | children->destroy(children); | |
23b1f713 MW |
1981 | |
1982 | if (id && !others) | |
1983 | { | |
1984 | /* found matching children only, delete full IKE_SA */ | |
1985 | id = ike_sa->get_unique_id(ike_sa); | |
1986 | array_insert_create_value(&ikeids, sizeof(id), | |
1987 | ARRAY_TAIL, &id); | |
1988 | } | |
1989 | else | |
1990 | { | |
1991 | children = ike_sa->create_child_sa_enumerator(ike_sa); | |
1992 | while (children->enumerate(children, &child_sa)) | |
1993 | { | |
1994 | if (streq(name, child_sa->get_name(child_sa))) | |
1995 | { | |
1996 | id = child_sa->get_unique_id(child_sa); | |
1997 | array_insert_create_value(&ids, sizeof(id), | |
1998 | ARRAY_TAIL, &id); | |
1999 | } | |
2000 | } | |
2001 | children->destroy(children); | |
2002 | } | |
7de35b7f MW |
2003 | } |
2004 | enumerator->destroy(enumerator); | |
2005 | ||
971a9168 | 2006 | if (array_count(ids)) |
7de35b7f | 2007 | { |
2facf188 | 2008 | while (array_remove(ids, ARRAY_HEAD, &id)) |
7de35b7f | 2009 | { |
2facf188 | 2010 | DBG1(DBG_CFG, "closing '%s' #%u", name, id); |
7de35b7f | 2011 | charon->controller->terminate_child(charon->controller, |
2facf188 | 2012 | id, NULL, NULL, 0); |
7de35b7f | 2013 | } |
971a9168 | 2014 | array_destroy(ids); |
7de35b7f | 2015 | } |
23b1f713 MW |
2016 | if (array_count(ikeids)) |
2017 | { | |
2018 | while (array_remove(ikeids, ARRAY_HEAD, &id)) | |
2019 | { | |
2020 | DBG1(DBG_CFG, "closing IKE_SA #%u", id); | |
2021 | charon->controller->terminate_ike(charon->controller, | |
2022 | id, NULL, NULL, 0); | |
2023 | } | |
2024 | array_destroy(ikeids); | |
2025 | } | |
7de35b7f MW |
2026 | break; |
2027 | case ACTION_ROUTE: | |
2028 | DBG1(DBG_CFG, "uninstalling '%s'", name); | |
2029 | switch (child_cfg->get_mode(child_cfg)) | |
2030 | { | |
2031 | case MODE_PASS: | |
2032 | case MODE_DROP: | |
7627f5f9 | 2033 | charon->shunts->uninstall(charon->shunts, peer_name, name); |
7de35b7f MW |
2034 | break; |
2035 | default: | |
2036 | enumerator = charon->traps->create_enumerator(charon->traps); | |
7627f5f9 TB |
2037 | while (enumerator->enumerate(enumerator, &peer_cfg, |
2038 | &child_sa)) | |
7de35b7f | 2039 | { |
7627f5f9 TB |
2040 | if (streq(peer_name, peer_cfg->get_name(peer_cfg)) && |
2041 | streq(name, child_sa->get_name(child_sa))) | |
7de35b7f | 2042 | { |
971a9168 | 2043 | id = child_sa->get_reqid(child_sa); |
7de35b7f MW |
2044 | break; |
2045 | } | |
2046 | } | |
2047 | enumerator->destroy(enumerator); | |
971a9168 | 2048 | if (id) |
7de35b7f | 2049 | { |
971a9168 | 2050 | charon->traps->uninstall(charon->traps, id); |
7de35b7f MW |
2051 | } |
2052 | break; | |
2053 | } | |
2054 | break; | |
2055 | default: | |
2056 | break; | |
2057 | } | |
2058 | } | |
2059 | ||
2060 | /** | |
20df9d31 | 2061 | * Run or undo a start actions associated with a child config |
7de35b7f | 2062 | */ |
20df9d31 TB |
2063 | static void handle_start_action(private_vici_config_t *this, |
2064 | peer_cfg_t *peer_cfg, child_cfg_t *child_cfg, | |
2065 | bool undo) | |
7de35b7f | 2066 | { |
20df9d31 TB |
2067 | this->handling_actions = TRUE; |
2068 | this->lock->unlock(this->lock); | |
7de35b7f | 2069 | |
20df9d31 TB |
2070 | if (undo) |
2071 | { | |
2072 | clear_start_action(this, peer_cfg->get_name(peer_cfg), child_cfg); | |
2073 | } | |
2074 | else | |
7de35b7f MW |
2075 | { |
2076 | run_start_action(this, peer_cfg, child_cfg); | |
2077 | } | |
20df9d31 TB |
2078 | |
2079 | this->lock->write_lock(this->lock); | |
2080 | this->handling_actions = FALSE; | |
7de35b7f MW |
2081 | } |
2082 | ||
2083 | /** | |
20df9d31 | 2084 | * Run or undo start actions associated with all child configs of a peer config |
7de35b7f | 2085 | */ |
20df9d31 TB |
2086 | static void handle_start_actions(private_vici_config_t *this, |
2087 | peer_cfg_t *peer_cfg, bool undo) | |
7de35b7f MW |
2088 | { |
2089 | enumerator_t *enumerator; | |
2090 | child_cfg_t *child_cfg; | |
2091 | ||
20df9d31 TB |
2092 | this->handling_actions = TRUE; |
2093 | this->lock->unlock(this->lock); | |
2094 | ||
7de35b7f MW |
2095 | enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); |
2096 | while (enumerator->enumerate(enumerator, &child_cfg)) | |
2097 | { | |
20df9d31 TB |
2098 | if (undo) |
2099 | { | |
2100 | clear_start_action(this, peer_cfg->get_name(peer_cfg), child_cfg); | |
2101 | } | |
2102 | else | |
2103 | { | |
2104 | run_start_action(this, peer_cfg, child_cfg); | |
2105 | } | |
7de35b7f MW |
2106 | } |
2107 | enumerator->destroy(enumerator); | |
20df9d31 TB |
2108 | |
2109 | this->lock->write_lock(this->lock); | |
2110 | this->handling_actions = FALSE; | |
7de35b7f MW |
2111 | } |
2112 | ||
b3d8bd8d MW |
2113 | /** |
2114 | * Replace children of a peer config by a new config | |
2115 | */ | |
2116 | static void replace_children(private_vici_config_t *this, | |
2117 | peer_cfg_t *from, peer_cfg_t *to) | |
2118 | { | |
2119 | enumerator_t *enumerator; | |
2120 | child_cfg_t *child; | |
101abed5 | 2121 | bool added; |
b3d8bd8d | 2122 | |
101abed5 TB |
2123 | enumerator = to->replace_child_cfgs(to, from); |
2124 | while (enumerator->enumerate(enumerator, &child, &added)) | |
b3d8bd8d | 2125 | { |
20df9d31 | 2126 | handle_start_action(this, to, child, !added); |
b3d8bd8d MW |
2127 | } |
2128 | enumerator->destroy(enumerator); | |
2129 | } | |
2130 | ||
2131 | /** | |
2132 | * Merge/replace a peer config with existing configs | |
2133 | */ | |
2134 | static void merge_config(private_vici_config_t *this, peer_cfg_t *peer_cfg) | |
2135 | { | |
2136 | enumerator_t *enumerator; | |
2137 | peer_cfg_t *current; | |
2138 | ike_cfg_t *ike_cfg; | |
2139 | bool merged = FALSE; | |
2140 | ||
2141 | this->lock->write_lock(this->lock); | |
20df9d31 TB |
2142 | while (this->handling_actions) |
2143 | { | |
2144 | this->condvar->wait(this->condvar, this->lock); | |
2145 | } | |
b3d8bd8d MW |
2146 | |
2147 | enumerator = this->conns->create_enumerator(this->conns); | |
2148 | while (enumerator->enumerate(enumerator, ¤t)) | |
2149 | { | |
2150 | if (streq(peer_cfg->get_name(peer_cfg), current->get_name(current))) | |
2151 | { | |
2152 | ike_cfg = current->get_ike_cfg(current); | |
2153 | if (peer_cfg->equals(peer_cfg, current) && | |
2154 | ike_cfg->equals(ike_cfg, peer_cfg->get_ike_cfg(peer_cfg))) | |
2155 | { | |
2156 | DBG1(DBG_CFG, "updated vici connection: %s", | |
2157 | peer_cfg->get_name(peer_cfg)); | |
2158 | replace_children(this, peer_cfg, current); | |
2159 | peer_cfg->destroy(peer_cfg); | |
2160 | } | |
2161 | else | |
2162 | { | |
2163 | DBG1(DBG_CFG, "replaced vici connection: %s", | |
2164 | peer_cfg->get_name(peer_cfg)); | |
2165 | this->conns->remove_at(this->conns, enumerator); | |
b3d8bd8d | 2166 | this->conns->insert_last(this->conns, peer_cfg); |
20df9d31 TB |
2167 | handle_start_actions(this, current, TRUE); |
2168 | handle_start_actions(this, peer_cfg, FALSE); | |
2169 | current->destroy(current); | |
b3d8bd8d MW |
2170 | } |
2171 | merged = TRUE; | |
2172 | break; | |
2173 | } | |
b3d8bd8d MW |
2174 | } |
2175 | enumerator->destroy(enumerator); | |
2176 | ||
2177 | if (!merged) | |
2178 | { | |
2179 | DBG1(DBG_CFG, "added vici connection: %s", peer_cfg->get_name(peer_cfg)); | |
2180 | this->conns->insert_last(this->conns, peer_cfg); | |
20df9d31 | 2181 | handle_start_actions(this, peer_cfg, FALSE); |
b3d8bd8d | 2182 | } |
20df9d31 | 2183 | this->condvar->signal(this->condvar); |
b3d8bd8d MW |
2184 | this->lock->unlock(this->lock); |
2185 | } | |
2186 | ||
2187 | CALLBACK(config_sn, bool, | |
2188 | request_data_t *request, vici_message_t *message, | |
2189 | vici_parse_context_t *ctx, char *name) | |
2190 | { | |
2191 | peer_data_t peer = { | |
2192 | .request = request, | |
2193 | .local = linked_list_create(), | |
2194 | .remote = linked_list_create(), | |
2195 | .vips = linked_list_create(), | |
2196 | .children = linked_list_create(), | |
2197 | .proposals = linked_list_create(), | |
2198 | .mobike = TRUE, | |
2199 | .send_certreq = TRUE, | |
2200 | .pull = TRUE, | |
2201 | .send_cert = CERT_SEND_IF_ASKED, | |
2202 | .version = IKE_ANY, | |
b3d8bd8d | 2203 | .remote_port = IKEV2_UDP_PORT, |
d5c6a0ba | 2204 | .fragmentation = FRAGMENTATION_YES, |
b3d8bd8d MW |
2205 | .unique = UNIQUE_NO, |
2206 | .keyingtries = 1, | |
f6511e36 MW |
2207 | .rekey_time = LFT_UNDEFINED, |
2208 | .reauth_time = LFT_UNDEFINED, | |
c5205105 | 2209 | .over_time = LFT_UNDEFINED, |
e651afe6 | 2210 | .rand_time = LFT_UNDEFINED, |
b3d8bd8d | 2211 | }; |
afb8f492 | 2212 | enumerator_t *enumerator; |
2ba5dadb | 2213 | peer_cfg_create_t cfg; |
b3d8bd8d MW |
2214 | peer_cfg_t *peer_cfg; |
2215 | ike_cfg_t *ike_cfg; | |
2216 | child_cfg_t *child_cfg; | |
229cdf6b | 2217 | auth_data_t *auth; |
b3d8bd8d | 2218 | proposal_t *proposal; |
3ad9c34c | 2219 | host_t *host; |
afb8f492 | 2220 | char *str; |
b3d8bd8d MW |
2221 | |
2222 | DBG2(DBG_CFG, " conn %s:", name); | |
2223 | ||
2224 | if (!message->parse(message, ctx, peer_sn, peer_kv, peer_li, &peer)) | |
2225 | { | |
2226 | free_peer_data(&peer); | |
2227 | return FALSE; | |
2228 | } | |
2229 | ||
2230 | if (peer.local->get_count(peer.local) == 0) | |
2231 | { | |
229cdf6b TB |
2232 | INIT(auth, |
2233 | .cfg = auth_cfg_create(), | |
2234 | ); | |
2235 | peer.local->insert_last(peer.local, auth); | |
b3d8bd8d MW |
2236 | } |
2237 | if (peer.remote->get_count(peer.remote) == 0) | |
2238 | { | |
229cdf6b TB |
2239 | INIT(auth, |
2240 | .cfg = auth_cfg_create(), | |
2241 | ); | |
2242 | peer.remote->insert_last(peer.remote, auth); | |
b3d8bd8d MW |
2243 | } |
2244 | if (peer.proposals->get_count(peer.proposals) == 0) | |
2245 | { | |
879e3d12 MW |
2246 | proposal = proposal_create_default(PROTO_IKE); |
2247 | if (proposal) | |
2248 | { | |
2249 | peer.proposals->insert_last(peer.proposals, proposal); | |
2250 | } | |
2251 | proposal = proposal_create_default_aead(PROTO_IKE); | |
2252 | if (proposal) | |
2253 | { | |
2254 | peer.proposals->insert_last(peer.proposals, proposal); | |
2255 | } | |
b3d8bd8d MW |
2256 | } |
2257 | if (!peer.local_addrs) | |
2258 | { | |
2259 | peer.local_addrs = strdup("%any"); | |
2260 | } | |
2261 | if (!peer.remote_addrs) | |
2262 | { | |
2263 | peer.remote_addrs = strdup("%any"); | |
2264 | } | |
682c9966 MW |
2265 | if (!peer.local_port) |
2266 | { | |
2267 | peer.local_port = charon->socket->get_port(charon->socket, FALSE); | |
2268 | } | |
b3d8bd8d | 2269 | |
f6511e36 MW |
2270 | if (peer.rekey_time == LFT_UNDEFINED && peer.reauth_time == LFT_UNDEFINED) |
2271 | { | |
2272 | /* apply a default rekey time if no rekey/reauth time set */ | |
b1df6312 | 2273 | peer.rekey_time = LFT_DEFAULT_IKE_REKEY_TIME; |
f6511e36 MW |
2274 | peer.reauth_time = 0; |
2275 | } | |
2276 | if (peer.rekey_time == LFT_UNDEFINED) | |
2277 | { | |
2278 | peer.rekey_time = 0; | |
2279 | } | |
2280 | if (peer.reauth_time == LFT_UNDEFINED) | |
2281 | { | |
2282 | peer.reauth_time = 0; | |
2283 | } | |
c5205105 MW |
2284 | if (peer.over_time == LFT_UNDEFINED) |
2285 | { | |
2286 | /* default over_time to 10% of rekey/reauth time if not given */ | |
2287 | peer.over_time = max(peer.rekey_time, peer.reauth_time) / 10; | |
2288 | } | |
e651afe6 MW |
2289 | if (peer.rand_time == LFT_UNDEFINED) |
2290 | { | |
1549a984 MW |
2291 | /* default rand_time to over_time if not given, but don't make it |
2292 | * longer than half of rekey/rauth time */ | |
2293 | if (peer.rekey_time && peer.reauth_time) | |
2294 | { | |
2295 | peer.rand_time = min(peer.rekey_time, peer.reauth_time); | |
2296 | } | |
2297 | else | |
2298 | { | |
2299 | peer.rand_time = max(peer.rekey_time, peer.reauth_time); | |
2300 | } | |
2301 | peer.rand_time = min(peer.over_time, peer.rand_time / 2); | |
e651afe6 | 2302 | } |
c5205105 | 2303 | |
f927ba97 TB |
2304 | #ifdef ME |
2305 | if (peer.mediation && peer.mediated_by) | |
2306 | { | |
2307 | DBG1(DBG_CFG, "a mediation connection cannot be a mediated connection " | |
2308 | "at the same time, config discarded"); | |
2309 | free_peer_data(&peer); | |
2310 | return FALSE; | |
2311 | } | |
2312 | if (peer.mediation) | |
2313 | { /* force unique connections for mediation connections */ | |
2314 | peer.unique = UNIQUE_REPLACE; | |
2315 | } | |
2316 | else if (peer.mediated_by) | |
2317 | { /* fallback to remote identity of first auth round if peer_id is not | |
2318 | * given explicitly */ | |
2319 | auth_cfg_t *cfg; | |
2320 | ||
2321 | if (!peer.peer_id && | |
2322 | peer.remote->get_first(peer.remote, (void**)&cfg) == SUCCESS) | |
2323 | { | |
2324 | peer.peer_id = cfg->get(cfg, AUTH_RULE_IDENTITY); | |
2325 | if (peer.peer_id) | |
2326 | { | |
2327 | peer.peer_id = peer.peer_id->clone(peer.peer_id); | |
2328 | } | |
2329 | else | |
2330 | { | |
2331 | DBG1(DBG_CFG, "mediation peer missing for mediated connection, " | |
2332 | "config discarded"); | |
2333 | free_peer_data(&peer); | |
2334 | return FALSE; | |
2335 | } | |
2336 | } | |
2337 | } | |
2338 | #endif /* ME */ | |
2339 | ||
b3d8bd8d MW |
2340 | log_peer_data(&peer); |
2341 | ||
2342 | ike_cfg = ike_cfg_create(peer.version, peer.send_certreq, peer.encap, | |
2343 | peer.local_addrs, peer.local_port, | |
2344 | peer.remote_addrs, peer.remote_port, | |
44fcc833 | 2345 | peer.fragmentation, peer.dscp); |
2ba5dadb TB |
2346 | |
2347 | cfg = (peer_cfg_create_t){ | |
2348 | .cert_policy = peer.send_cert, | |
2349 | .unique = peer.unique, | |
2350 | .keyingtries = peer.keyingtries, | |
2351 | .rekey_time = peer.rekey_time, | |
2352 | .reauth_time = peer.reauth_time, | |
2353 | .jitter_time = peer.rand_time, | |
2354 | .over_time = peer.over_time, | |
2355 | .no_mobike = !peer.mobike, | |
2356 | .aggressive = peer.aggressive, | |
2357 | .push_mode = !peer.pull, | |
2358 | .dpd = peer.dpd_delay, | |
2359 | .dpd_timeout = peer.dpd_timeout, | |
2360 | }; | |
f927ba97 TB |
2361 | #ifdef ME |
2362 | cfg.mediation = peer.mediation; | |
2363 | if (peer.mediated_by) | |
2364 | { | |
2365 | cfg.mediated_by = peer.mediated_by; | |
2366 | cfg.peer_id = peer.peer_id->clone(peer.peer_id); | |
2367 | } | |
2368 | #endif /* ME */ | |
2ba5dadb | 2369 | peer_cfg = peer_cfg_create(name, ike_cfg, &cfg); |
b3d8bd8d MW |
2370 | |
2371 | while (peer.local->remove_first(peer.local, | |
229cdf6b | 2372 | (void**)&auth) == SUCCESS) |
b3d8bd8d | 2373 | { |
229cdf6b TB |
2374 | peer_cfg->add_auth_cfg(peer_cfg, auth->cfg, TRUE); |
2375 | auth->cfg = NULL; | |
2376 | free_auth_data(auth); | |
b3d8bd8d MW |
2377 | } |
2378 | while (peer.remote->remove_first(peer.remote, | |
229cdf6b | 2379 | (void**)&auth) == SUCCESS) |
b3d8bd8d | 2380 | { |
229cdf6b TB |
2381 | peer_cfg->add_auth_cfg(peer_cfg, auth->cfg, FALSE); |
2382 | auth->cfg = NULL; | |
2383 | free_auth_data(auth); | |
b3d8bd8d MW |
2384 | } |
2385 | while (peer.children->remove_first(peer.children, | |
2386 | (void**)&child_cfg) == SUCCESS) | |
2387 | { | |
2388 | peer_cfg->add_child_cfg(peer_cfg, child_cfg); | |
2389 | } | |
2390 | while (peer.proposals->remove_first(peer.proposals, | |
2391 | (void**)&proposal) == SUCCESS) | |
2392 | { | |
2393 | ike_cfg->add_proposal(ike_cfg, proposal); | |
2394 | } | |
3ad9c34c MW |
2395 | while (peer.vips->remove_first(peer.vips, (void**)&host) == SUCCESS) |
2396 | { | |
2397 | peer_cfg->add_virtual_ip(peer_cfg, host); | |
2398 | } | |
afb8f492 MW |
2399 | if (peer.pools) |
2400 | { | |
2401 | enumerator = enumerator_create_token(peer.pools, ",", " "); | |
2402 | while (enumerator->enumerate(enumerator, &str)) | |
2403 | { | |
2404 | peer_cfg->add_pool(peer_cfg, str); | |
2405 | } | |
2406 | enumerator->destroy(enumerator); | |
2407 | } | |
b3d8bd8d MW |
2408 | |
2409 | free_peer_data(&peer); | |
2410 | ||
2411 | merge_config(request->this, peer_cfg); | |
2412 | ||
2413 | return TRUE; | |
2414 | } | |
2415 | ||
2416 | CALLBACK(load_conn, vici_message_t*, | |
2417 | private_vici_config_t *this, char *name, u_int id, vici_message_t *message) | |
2418 | { | |
2419 | request_data_t request = { | |
2420 | .this = this, | |
2421 | }; | |
2422 | ||
2423 | if (!message->parse(message, NULL, config_sn, NULL, NULL, &request)) | |
2424 | { | |
2425 | if (request.reply) | |
2426 | { | |
2427 | return request.reply; | |
2428 | } | |
2429 | return create_reply("parsing request failed"); | |
2430 | } | |
2431 | return create_reply(NULL); | |
2432 | } | |
2433 | ||
501ddf12 MW |
2434 | CALLBACK(unload_conn, vici_message_t*, |
2435 | private_vici_config_t *this, char *name, u_int id, vici_message_t *message) | |
2436 | { | |
2437 | enumerator_t *enumerator; | |
2438 | peer_cfg_t *cfg; | |
e1943491 | 2439 | char *conn_name; |
501ddf12 | 2440 | bool found = FALSE; |
501ddf12 | 2441 | |
e1943491 AS |
2442 | conn_name = message->get_str(message, NULL, "name"); |
2443 | if (!conn_name) | |
501ddf12 | 2444 | { |
e1943491 | 2445 | return create_reply("unload: missing connection name"); |
501ddf12 MW |
2446 | } |
2447 | ||
2448 | this->lock->write_lock(this->lock); | |
20df9d31 TB |
2449 | while (this->handling_actions) |
2450 | { | |
2451 | this->condvar->wait(this->condvar, this->lock); | |
2452 | } | |
501ddf12 MW |
2453 | enumerator = this->conns->create_enumerator(this->conns); |
2454 | while (enumerator->enumerate(enumerator, &cfg)) | |
2455 | { | |
e1943491 | 2456 | if (streq(cfg->get_name(cfg), conn_name)) |
501ddf12 MW |
2457 | { |
2458 | this->conns->remove_at(this->conns, enumerator); | |
20df9d31 | 2459 | handle_start_actions(this, cfg, TRUE); |
501ddf12 MW |
2460 | cfg->destroy(cfg); |
2461 | found = TRUE; | |
2462 | break; | |
2463 | } | |
2464 | } | |
2465 | enumerator->destroy(enumerator); | |
20df9d31 | 2466 | this->condvar->signal(this->condvar); |
501ddf12 MW |
2467 | this->lock->unlock(this->lock); |
2468 | ||
2469 | if (!found) | |
2470 | { | |
e1943491 | 2471 | return create_reply("unload: connection '%s' not found", conn_name); |
501ddf12 MW |
2472 | } |
2473 | return create_reply(NULL); | |
2474 | } | |
2475 | ||
2476 | CALLBACK(get_conns, vici_message_t*, | |
2477 | private_vici_config_t *this, char *name, u_int id, vici_message_t *message) | |
2478 | { | |
2479 | vici_builder_t *builder; | |
2480 | enumerator_t *enumerator; | |
2481 | peer_cfg_t *cfg; | |
2482 | ||
2483 | builder = vici_builder_create(); | |
2484 | builder->begin_list(builder, "conns"); | |
2485 | ||
2486 | this->lock->read_lock(this->lock); | |
2487 | enumerator = this->conns->create_enumerator(this->conns); | |
2488 | while (enumerator->enumerate(enumerator, &cfg)) | |
2489 | { | |
2490 | builder->add_li(builder, "%s", cfg->get_name(cfg)); | |
2491 | } | |
2492 | enumerator->destroy(enumerator); | |
2493 | this->lock->unlock(this->lock); | |
2494 | ||
2495 | builder->end_list(builder); | |
2496 | ||
2497 | return builder->finalize(builder); | |
2498 | } | |
2499 | ||
b3d8bd8d MW |
2500 | static void manage_command(private_vici_config_t *this, |
2501 | char *name, vici_command_cb_t cb, bool reg) | |
2502 | { | |
2503 | this->dispatcher->manage_command(this->dispatcher, name, | |
2504 | reg ? cb : NULL, this); | |
2505 | } | |
2506 | ||
2507 | /** | |
2508 | * (Un-)register dispatcher functions | |
2509 | */ | |
2510 | static void manage_commands(private_vici_config_t *this, bool reg) | |
2511 | { | |
2512 | manage_command(this, "load-conn", load_conn, reg); | |
501ddf12 MW |
2513 | manage_command(this, "unload-conn", unload_conn, reg); |
2514 | manage_command(this, "get-conns", get_conns, reg); | |
b3d8bd8d MW |
2515 | } |
2516 | ||
2517 | METHOD(vici_config_t, destroy, void, | |
2518 | private_vici_config_t *this) | |
2519 | { | |
2520 | manage_commands(this, FALSE); | |
2521 | this->conns->destroy_offset(this->conns, offsetof(peer_cfg_t, destroy)); | |
20df9d31 | 2522 | this->condvar->destroy(this->condvar); |
b3d8bd8d MW |
2523 | this->lock->destroy(this->lock); |
2524 | free(this); | |
2525 | } | |
2526 | ||
2527 | /** | |
2528 | * See header | |
2529 | */ | |
63d37038 | 2530 | vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher, |
87371460 AS |
2531 | vici_authority_t *authority, |
2532 | vici_cred_t *cred) | |
b3d8bd8d MW |
2533 | { |
2534 | private_vici_config_t *this; | |
2535 | ||
2536 | INIT(this, | |
2537 | .public = { | |
2538 | .backend = { | |
2539 | .create_peer_cfg_enumerator = _create_peer_cfg_enumerator, | |
2540 | .create_ike_cfg_enumerator = _create_ike_cfg_enumerator, | |
2541 | .get_peer_cfg_by_name = _get_peer_cfg_by_name, | |
2542 | }, | |
2543 | .destroy = _destroy, | |
2544 | }, | |
2545 | .dispatcher = dispatcher, | |
2546 | .conns = linked_list_create(), | |
2547 | .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), | |
20df9d31 | 2548 | .condvar = rwlock_condvar_create(), |
63d37038 | 2549 | .authority = authority, |
87371460 | 2550 | .cred = cred, |
b3d8bd8d MW |
2551 | ); |
2552 | ||
2553 | manage_commands(this, TRUE); | |
2554 | ||
2555 | return &this->public; | |
2556 | } |