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