]>
Commit | Line | Data |
---|---|---|
c60c7694 MW |
1 | /* |
2 | * Copyright (C) 2007 Martin Willi | |
3 | * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser | |
4 | * Hochschule fuer Technik Rapperswil | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include "ike_config.h" | |
18 | ||
19 | #include <daemon.h> | |
20 | #include <encoding/payloads/cp_payload.h> | |
21 | ||
22 | typedef struct private_ike_config_t private_ike_config_t; | |
23 | ||
24 | /** | |
25 | * Private members of a ike_config_t task. | |
26 | */ | |
27 | struct private_ike_config_t { | |
7daf5226 | 28 | |
c60c7694 MW |
29 | /** |
30 | * Public methods and task_t interface. | |
31 | */ | |
32 | ike_config_t public; | |
7daf5226 | 33 | |
c60c7694 MW |
34 | /** |
35 | * Assigned IKE_SA. | |
36 | */ | |
37 | ike_sa_t *ike_sa; | |
7daf5226 | 38 | |
c60c7694 MW |
39 | /** |
40 | * Are we the initiator? | |
41 | */ | |
42 | bool initiator; | |
7daf5226 | 43 | |
c60c7694 MW |
44 | /** |
45 | * virtual ip | |
46 | */ | |
47 | host_t *virtual_ip; | |
b5a2055f MW |
48 | |
49 | /** | |
50 | * list of attributes requested and its handler, entry_t | |
51 | */ | |
52 | linked_list_t *requested; | |
c60c7694 MW |
53 | }; |
54 | ||
55 | /** | |
b5a2055f MW |
56 | * Entry for a requested attribute and the requesting handler |
57 | */ | |
58 | typedef struct { | |
59 | /** attribute requested */ | |
60 | configuration_attribute_type_t type; | |
61 | /** handler requesting this attribute */ | |
62 | attribute_handler_t *handler; | |
63 | } entry_t; | |
64 | ||
65 | /** | |
66 | * build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip | |
c60c7694 | 67 | */ |
b5a2055f | 68 | static configuration_attribute_t *build_vip(host_t *vip) |
c60c7694 | 69 | { |
b5a2055f | 70 | configuration_attribute_type_t type; |
c60c7694 | 71 | chunk_t chunk, prefix; |
7daf5226 | 72 | |
7f56b494 | 73 | if (vip->get_family(vip) == AF_INET) |
c60c7694 | 74 | { |
b5a2055f | 75 | type = INTERNAL_IP4_ADDRESS; |
7f56b494 | 76 | if (vip->is_anyaddr(vip)) |
c60c7694 MW |
77 | { |
78 | chunk = chunk_empty; | |
79 | } | |
80 | else | |
81 | { | |
7f56b494 | 82 | chunk = vip->get_address(vip); |
c60c7694 MW |
83 | } |
84 | } | |
85 | else | |
86 | { | |
b5a2055f | 87 | type = INTERNAL_IP6_ADDRESS; |
7f56b494 | 88 | if (vip->is_anyaddr(vip)) |
c60c7694 MW |
89 | { |
90 | chunk = chunk_empty; | |
91 | } | |
92 | else | |
93 | { | |
94 | prefix = chunk_alloca(1); | |
95 | *prefix.ptr = 64; | |
7f56b494 | 96 | chunk = vip->get_address(vip); |
c60c7694 MW |
97 | chunk = chunk_cata("cc", chunk, prefix); |
98 | } | |
99 | } | |
b5a2055f MW |
100 | return configuration_attribute_create_value(type, chunk); |
101 | } | |
102 | ||
103 | /** | |
104 | * Handle a received attribute as initiator | |
105 | */ | |
106 | static void handle_attribute(private_ike_config_t *this, | |
107 | configuration_attribute_t *ca) | |
108 | { | |
109 | attribute_handler_t *handler = NULL; | |
110 | enumerator_t *enumerator; | |
111 | entry_t *entry; | |
112 | ||
113 | /* find the handler which requested this attribute */ | |
114 | enumerator = this->requested->create_enumerator(this->requested); | |
115 | while (enumerator->enumerate(enumerator, &entry)) | |
116 | { | |
117 | if (entry->type == ca->get_type(ca)) | |
118 | { | |
119 | handler = entry->handler; | |
120 | this->requested->remove_at(this->requested, enumerator); | |
121 | free(entry); | |
122 | break; | |
123 | } | |
124 | } | |
1427c93f | 125 | enumerator->destroy(enumerator); |
b5a2055f MW |
126 | |
127 | /* and pass it to the handle function */ | |
128 | handler = lib->attributes->handle(lib->attributes, | |
129 | this->ike_sa->get_other_id(this->ike_sa), handler, | |
130 | ca->get_type(ca), ca->get_value(ca)); | |
131 | if (handler) | |
132 | { | |
133 | this->ike_sa->add_configuration_attribute(this->ike_sa, | |
134 | handler, ca->get_type(ca), ca->get_value(ca)); | |
135 | } | |
c60c7694 MW |
136 | } |
137 | ||
138 | /** | |
139 | * process a single configuration attribute | |
140 | */ | |
141 | static void process_attribute(private_ike_config_t *this, | |
142 | configuration_attribute_t *ca) | |
143 | { | |
144 | host_t *ip; | |
145 | chunk_t addr; | |
146 | int family = AF_INET6; | |
7daf5226 | 147 | |
c60c7694 MW |
148 | switch (ca->get_type(ca)) |
149 | { | |
150 | case INTERNAL_IP4_ADDRESS: | |
151 | family = AF_INET; | |
152 | /* fall */ | |
153 | case INTERNAL_IP6_ADDRESS: | |
154 | { | |
155 | addr = ca->get_value(ca); | |
156 | if (addr.len == 0) | |
157 | { | |
158 | ip = host_create_any(family); | |
159 | } | |
160 | else | |
161 | { | |
162 | /* skip prefix byte in IPv6 payload*/ | |
163 | if (family == AF_INET6) | |
164 | { | |
7daf5226 | 165 | addr.len--; |
c60c7694 MW |
166 | } |
167 | ip = host_create_from_chunk(family, addr, 0); | |
168 | } | |
c60c7694 MW |
169 | if (ip) |
170 | { | |
7f56b494 MW |
171 | DESTROY_IF(this->virtual_ip); |
172 | this->virtual_ip = ip; | |
c60c7694 MW |
173 | } |
174 | break; | |
175 | } | |
7f56b494 | 176 | default: |
b5a2055f | 177 | { |
7f56b494 | 178 | if (this->initiator) |
2b1f5f34 | 179 | { |
b5a2055f | 180 | handle_attribute(this, ca); |
2b1f5f34 | 181 | } |
b5a2055f | 182 | } |
c60c7694 MW |
183 | } |
184 | } | |
185 | ||
186 | /** | |
187 | * Scan for configuration payloads and attributes | |
188 | */ | |
189 | static void process_payloads(private_ike_config_t *this, message_t *message) | |
190 | { | |
b5a2055f | 191 | enumerator_t *enumerator, *attributes; |
c60c7694 | 192 | payload_t *payload; |
7daf5226 | 193 | |
a44bb934 MW |
194 | enumerator = message->create_payload_enumerator(message); |
195 | while (enumerator->enumerate(enumerator, &payload)) | |
c60c7694 MW |
196 | { |
197 | if (payload->get_type(payload) == CONFIGURATION) | |
198 | { | |
199 | cp_payload_t *cp = (cp_payload_t*)payload; | |
200 | configuration_attribute_t *ca; | |
b5a2055f MW |
201 | |
202 | switch (cp->get_type(cp)) | |
c60c7694 MW |
203 | { |
204 | case CFG_REQUEST: | |
205 | case CFG_REPLY: | |
206 | { | |
b5a2055f MW |
207 | attributes = cp->create_attribute_enumerator(cp); |
208 | while (attributes->enumerate(attributes, &ca)) | |
c60c7694 | 209 | { |
653da7c9 AS |
210 | DBG2(DBG_IKE, "processing %N config attribute", |
211 | configuration_attribute_type_names, ca->get_type(ca)); | |
c60c7694 MW |
212 | process_attribute(this, ca); |
213 | } | |
214 | attributes->destroy(attributes); | |
215 | break; | |
216 | } | |
217 | default: | |
7daf5226 | 218 | DBG1(DBG_IKE, "ignoring %N config payload", |
b5a2055f | 219 | config_type_names, cp->get_type(cp)); |
c60c7694 MW |
220 | break; |
221 | } | |
222 | } | |
223 | } | |
a44bb934 | 224 | enumerator->destroy(enumerator); |
c60c7694 MW |
225 | } |
226 | ||
227 | /** | |
228 | * Implementation of task_t.process for initiator | |
229 | */ | |
230 | static status_t build_i(private_ike_config_t *this, message_t *message) | |
231 | { | |
a44bb934 MW |
232 | if (message->get_message_id(message) == 1) |
233 | { /* in first IKE_AUTH only */ | |
b5a2055f MW |
234 | cp_payload_t *cp = NULL; |
235 | enumerator_t *enumerator; | |
236 | attribute_handler_t *handler; | |
00ccb876 | 237 | peer_cfg_t *config; |
b5a2055f MW |
238 | configuration_attribute_type_t type; |
239 | chunk_t data; | |
00ccb876 | 240 | host_t *vip; |
7daf5226 | 241 | |
00ccb876 MW |
242 | /* reuse virtual IP if we already have one */ |
243 | vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); | |
cdcfe777 | 244 | if (!vip) |
00ccb876 | 245 | { |
cdcfe777 MW |
246 | config = this->ike_sa->get_peer_cfg(this->ike_sa); |
247 | vip = config->get_virtual_ip(config); | |
00ccb876 | 248 | } |
cdcfe777 | 249 | if (vip) |
00ccb876 | 250 | { |
b5a2055f MW |
251 | cp = cp_payload_create_type(CFG_REQUEST); |
252 | cp->add_attribute(cp, build_vip(vip)); | |
253 | } | |
7daf5226 | 254 | |
b5a2055f MW |
255 | enumerator = lib->attributes->create_initiator_enumerator(lib->attributes, |
256 | this->ike_sa->get_other_id(this->ike_sa), vip); | |
257 | while (enumerator->enumerate(enumerator, &handler, &type, &data)) | |
258 | { | |
259 | configuration_attribute_t *ca; | |
260 | entry_t *entry; | |
7daf5226 | 261 | |
b5a2055f | 262 | /* create configuration attribute */ |
653da7c9 AS |
263 | DBG2(DBG_IKE, "building %N config attribute", |
264 | configuration_attribute_type_names, type); | |
b5a2055f MW |
265 | ca = configuration_attribute_create_value(type, data); |
266 | if (!cp) | |
7f56b494 | 267 | { |
b5a2055f | 268 | cp = cp_payload_create_type(CFG_REQUEST); |
7f56b494 | 269 | } |
b5a2055f MW |
270 | cp->add_attribute(cp, ca); |
271 | ||
272 | /* save handler along with requested type */ | |
273 | entry = malloc_thing(entry_t); | |
274 | entry->type = type; | |
275 | entry->handler = handler; | |
276 | ||
277 | this->requested->insert_last(this->requested, entry); | |
278 | } | |
279 | enumerator->destroy(enumerator); | |
280 | ||
281 | if (cp) | |
282 | { | |
7f56b494 | 283 | message->add_payload(message, (payload_t*)cp); |
00ccb876 | 284 | } |
c60c7694 | 285 | } |
c60c7694 MW |
286 | return NEED_MORE; |
287 | } | |
288 | ||
289 | /** | |
290 | * Implementation of task_t.process for responder | |
291 | */ | |
292 | static status_t process_r(private_ike_config_t *this, message_t *message) | |
293 | { | |
a44bb934 MW |
294 | if (message->get_message_id(message) == 1) |
295 | { /* in first IKE_AUTH only */ | |
c60c7694 MW |
296 | process_payloads(this, message); |
297 | } | |
298 | return NEED_MORE; | |
299 | } | |
300 | ||
5b4d0de7 MW |
301 | /** |
302 | * Find a peer (EAP) identity to query provider for attributes | |
303 | */ | |
304 | static identification_t *get_peer_identity(private_ike_config_t *this) | |
305 | { | |
306 | identification_t *id = NULL, *current; | |
307 | enumerator_t *enumerator; | |
308 | auth_cfg_t *cfg; | |
309 | ||
310 | enumerator = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE); | |
311 | while (enumerator->enumerate(enumerator, &cfg)) | |
312 | { | |
313 | /* prefer EAP-Identity of last round */ | |
314 | current = cfg->get(cfg, AUTH_RULE_EAP_IDENTITY); | |
315 | if (!current || current->get_type(current) == ID_ANY) | |
316 | { | |
317 | current = cfg->get(cfg, AUTH_RULE_IDENTITY); | |
318 | } | |
319 | if (current && current->get_type(current) != ID_ANY) | |
320 | { | |
321 | id = current; | |
322 | continue; | |
323 | } | |
324 | } | |
325 | enumerator->destroy(enumerator); | |
326 | if (!id) | |
327 | { /* fallback, should not happen */ | |
328 | id = this->ike_sa->get_other_id(this->ike_sa); | |
329 | } | |
330 | return id; | |
331 | } | |
332 | ||
c60c7694 MW |
333 | /** |
334 | * Implementation of task_t.build for responder | |
335 | */ | |
336 | static status_t build_r(private_ike_config_t *this, message_t *message) | |
337 | { | |
a44bb934 MW |
338 | if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) |
339 | { /* in last IKE_AUTH exchange */ | |
b5a2055f MW |
340 | enumerator_t *enumerator; |
341 | configuration_attribute_type_t type; | |
342 | chunk_t value; | |
343 | host_t *vip = NULL; | |
344 | cp_payload_t *cp = NULL; | |
345 | peer_cfg_t *config; | |
5b4d0de7 MW |
346 | identification_t *id; |
347 | ||
348 | id = get_peer_identity(this); | |
7daf5226 | 349 | |
b5a2055f | 350 | config = this->ike_sa->get_peer_cfg(this->ike_sa); |
e0fe7651 | 351 | if (config && this->virtual_ip) |
c60c7694 | 352 | { |
c60c7694 | 353 | DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); |
45819d7d MW |
354 | if (config->get_pool(config)) |
355 | { | |
930443af | 356 | vip = lib->attributes->acquire_address(lib->attributes, |
5b4d0de7 | 357 | config->get_pool(config), id, this->virtual_ip); |
45819d7d | 358 | } |
7f56b494 | 359 | if (vip == NULL) |
c60c7694 | 360 | { |
d510eaea MW |
361 | DBG1(DBG_IKE, "no virtual IP found, sending %N", |
362 | notify_type_names, INTERNAL_ADDRESS_FAILURE); | |
363 | message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE, | |
364 | chunk_empty); | |
c60c7694 MW |
365 | return SUCCESS; |
366 | } | |
7f56b494 MW |
367 | DBG1(DBG_IKE, "assigning virtual IP %H to peer", vip); |
368 | this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip); | |
7daf5226 | 369 | |
b5a2055f MW |
370 | cp = cp_payload_create_type(CFG_REPLY); |
371 | cp->add_attribute(cp, build_vip(vip)); | |
372 | } | |
7daf5226 | 373 | |
b5a2055f MW |
374 | /* query registered providers for additional attributes to include */ |
375 | enumerator = lib->attributes->create_responder_enumerator( | |
5b4d0de7 | 376 | lib->attributes, id, vip); |
b5a2055f MW |
377 | while (enumerator->enumerate(enumerator, &type, &value)) |
378 | { | |
379 | if (!cp) | |
7f56b494 | 380 | { |
b5a2055f | 381 | cp = cp_payload_create_type(CFG_REPLY); |
7f56b494 | 382 | } |
b5a2055f MW |
383 | cp->add_attribute(cp, |
384 | configuration_attribute_create_value(type, value)); | |
385 | } | |
386 | enumerator->destroy(enumerator); | |
7daf5226 | 387 | |
b5a2055f MW |
388 | if (cp) |
389 | { | |
7f56b494 | 390 | message->add_payload(message, (payload_t*)cp); |
c60c7694 | 391 | } |
b5a2055f | 392 | DESTROY_IF(vip); |
c60c7694 MW |
393 | return SUCCESS; |
394 | } | |
395 | return NEED_MORE; | |
396 | } | |
397 | ||
398 | /** | |
399 | * Implementation of task_t.process for initiator | |
400 | */ | |
401 | static status_t process_i(private_ike_config_t *this, message_t *message) | |
402 | { | |
a44bb934 MW |
403 | if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) |
404 | { /* in last IKE_AUTH exchange */ | |
7daf5226 | 405 | |
c60c7694 | 406 | process_payloads(this, message); |
7daf5226 | 407 | |
7f56b494 | 408 | if (this->virtual_ip) |
c60c7694 MW |
409 | { |
410 | this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); | |
c60c7694 MW |
411 | } |
412 | return SUCCESS; | |
413 | } | |
414 | return NEED_MORE; | |
415 | } | |
416 | ||
417 | /** | |
418 | * Implementation of task_t.get_type | |
419 | */ | |
420 | static task_type_t get_type(private_ike_config_t *this) | |
421 | { | |
422 | return IKE_CONFIG; | |
423 | } | |
424 | ||
425 | /** | |
426 | * Implementation of task_t.migrate | |
427 | */ | |
428 | static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa) | |
429 | { | |
430 | DESTROY_IF(this->virtual_ip); | |
7daf5226 | 431 | |
c60c7694 MW |
432 | this->ike_sa = ike_sa; |
433 | this->virtual_ip = NULL; | |
b5a2055f MW |
434 | this->requested->destroy_function(this->requested, free); |
435 | this->requested = linked_list_create(); | |
c60c7694 MW |
436 | } |
437 | ||
438 | /** | |
439 | * Implementation of task_t.destroy | |
440 | */ | |
441 | static void destroy(private_ike_config_t *this) | |
442 | { | |
443 | DESTROY_IF(this->virtual_ip); | |
b5a2055f | 444 | this->requested->destroy_function(this->requested, free); |
c60c7694 MW |
445 | free(this); |
446 | } | |
447 | ||
448 | /* | |
449 | * Described in header. | |
450 | */ | |
e0fe7651 | 451 | ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator) |
c60c7694 MW |
452 | { |
453 | private_ike_config_t *this = malloc_thing(private_ike_config_t); | |
7daf5226 | 454 | |
c60c7694 MW |
455 | this->public.task.get_type = (task_type_t(*)(task_t*))get_type; |
456 | this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; | |
457 | this->public.task.destroy = (void(*)(task_t*))destroy; | |
7daf5226 | 458 | |
48f9a22b AS |
459 | this->initiator = initiator; |
460 | this->ike_sa = ike_sa; | |
461 | this->virtual_ip = NULL; | |
b5a2055f | 462 | this->requested = linked_list_create(); |
7daf5226 | 463 | |
e0fe7651 | 464 | if (initiator) |
c60c7694 MW |
465 | { |
466 | this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; | |
467 | this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; | |
c60c7694 MW |
468 | } |
469 | else | |
470 | { | |
471 | this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; | |
472 | this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; | |
c60c7694 | 473 | } |
7daf5226 | 474 | |
c60c7694 MW |
475 | return &this->public; |
476 | } | |
7f56b494 | 477 |