]>
Commit | Line | Data |
---|---|---|
4a09d9ee | 1 | /* |
68c6863b TB |
2 | * Copyright (C) 2007-2011 Tobias Brunner |
3 | * Copyright (C) 2007-2011 Martin Willi | |
4a09d9ee MW |
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 "task_manager_v1.h" | |
18 | ||
da063ec9 MW |
19 | #include <math.h> |
20 | ||
4a09d9ee | 21 | #include <daemon.h> |
5d1eeec2 | 22 | #include <sa/tasks/child_delete.h> |
c73c832c | 23 | #include <sa/tasks/main_mode.h> |
744c0801 | 24 | #include <sa/tasks/quick_mode.h> |
69adeb5b | 25 | #include <sa/tasks/xauth.h> |
156b8662 | 26 | #include <sa/tasks/mode_config.h> |
5d1eeec2 | 27 | #include <sa/tasks/ike_delete.h> |
1cc4ec46 | 28 | #include <sa/tasks/ike_natd_v1.h> |
a2f8fc97 | 29 | #include <sa/tasks/ike_vendor_v1.h> |
0bcdb8e5 | 30 | #include <sa/tasks/ike_cert_pre_v1.h> |
c64a4b4f | 31 | #include <sa/tasks/ike_cert_post_v1.h> |
5d1eeec2 | 32 | #include <encoding/payloads/delete_payload.h> |
da063ec9 | 33 | #include <processing/jobs/retransmit_job.h> |
68c6863b | 34 | #include <processing/jobs/delete_ike_sa_job.h> |
4a09d9ee MW |
35 | |
36 | typedef struct exchange_t exchange_t; | |
37 | ||
38 | /** | |
39 | * An exchange in the air, used do detect and handle retransmission | |
40 | */ | |
41 | struct exchange_t { | |
42 | ||
43 | /** | |
44 | * Message ID used for this transaction | |
45 | */ | |
46 | u_int32_t mid; | |
47 | ||
48 | /** | |
49 | * generated packet for retransmission | |
50 | */ | |
51 | packet_t *packet; | |
52 | }; | |
53 | ||
54 | typedef struct private_task_manager_t private_task_manager_t; | |
55 | ||
56 | /** | |
57 | * private data of the task manager | |
58 | */ | |
59 | struct private_task_manager_t { | |
60 | ||
61 | /** | |
62 | * public functions | |
63 | */ | |
64 | task_manager_v1_t public; | |
65 | ||
66 | /** | |
67 | * associated IKE_SA we are serving | |
68 | */ | |
69 | ike_sa_t *ike_sa; | |
70 | ||
73aaf76b MW |
71 | /** |
72 | * RNG to create message IDs | |
73 | */ | |
74 | rng_t *rng; | |
75 | ||
4a09d9ee MW |
76 | /** |
77 | * Exchange we are currently handling as responder | |
78 | */ | |
79 | struct { | |
80 | /** | |
81 | * Message ID of the exchange | |
82 | */ | |
83 | u_int32_t mid; | |
84 | ||
9cc8bd4f MW |
85 | /** |
86 | * Hash of a previously received message | |
87 | */ | |
88 | u_int32_t hash; | |
89 | ||
4a09d9ee MW |
90 | /** |
91 | * packet for retransmission | |
92 | */ | |
93 | packet_t *packet; | |
94 | ||
95 | } responding; | |
96 | ||
97 | /** | |
98 | * Exchange we are currently handling as initiator | |
99 | */ | |
100 | struct { | |
101 | /** | |
102 | * Message ID of the exchange | |
103 | */ | |
104 | u_int32_t mid; | |
105 | ||
9cc8bd4f | 106 | /** |
751bd02e | 107 | * Sequence number of the last sent message |
9cc8bd4f | 108 | */ |
751bd02e | 109 | u_int32_t seqnr; |
9cc8bd4f | 110 | |
4a09d9ee MW |
111 | /** |
112 | * how many times we have retransmitted so far | |
113 | */ | |
114 | u_int retransmitted; | |
115 | ||
116 | /** | |
117 | * packet for retransmission | |
118 | */ | |
119 | packet_t *packet; | |
120 | ||
121 | /** | |
122 | * type of the initated exchange | |
123 | */ | |
124 | exchange_type_t type; | |
125 | ||
126 | } initiating; | |
127 | ||
128 | /** | |
129 | * List of queued tasks not yet in action | |
130 | */ | |
131 | linked_list_t *queued_tasks; | |
132 | ||
133 | /** | |
134 | * List of active tasks, initiated by ourselve | |
135 | */ | |
136 | linked_list_t *active_tasks; | |
137 | ||
138 | /** | |
139 | * List of tasks initiated by peer | |
140 | */ | |
141 | linked_list_t *passive_tasks; | |
142 | ||
4a09d9ee MW |
143 | /** |
144 | * Number of times we retransmit messages before giving up | |
145 | */ | |
146 | u_int retransmit_tries; | |
147 | ||
148 | /** | |
149 | * Retransmission timeout | |
150 | */ | |
151 | double retransmit_timeout; | |
152 | ||
153 | /** | |
154 | * Base to calculate retransmission timeout | |
155 | */ | |
156 | double retransmit_base; | |
157 | }; | |
158 | ||
159 | /** | |
160 | * flush all tasks in the task manager | |
161 | */ | |
162 | static void flush(private_task_manager_t *this) | |
163 | { | |
164 | this->queued_tasks->destroy_offset(this->queued_tasks, | |
165 | offsetof(task_t, destroy)); | |
166 | this->queued_tasks = linked_list_create(); | |
167 | this->passive_tasks->destroy_offset(this->passive_tasks, | |
168 | offsetof(task_t, destroy)); | |
169 | this->passive_tasks = linked_list_create(); | |
170 | this->active_tasks->destroy_offset(this->active_tasks, | |
171 | offsetof(task_t, destroy)); | |
172 | this->active_tasks = linked_list_create(); | |
173 | } | |
174 | ||
26b55dc6 MW |
175 | /** |
176 | * move a task of a specific type from the queue to the active list | |
177 | */ | |
178 | static bool activate_task(private_task_manager_t *this, task_type_t type) | |
179 | { | |
180 | enumerator_t *enumerator; | |
181 | task_t *task; | |
182 | bool found = FALSE; | |
183 | ||
184 | enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); | |
185 | while (enumerator->enumerate(enumerator, (void**)&task)) | |
186 | { | |
187 | if (task->get_type(task) == type) | |
188 | { | |
189 | DBG2(DBG_IKE, " activating %N task", task_type_names, type); | |
190 | this->queued_tasks->remove_at(this->queued_tasks, enumerator); | |
191 | this->active_tasks->insert_last(this->active_tasks, task); | |
192 | found = TRUE; | |
193 | break; | |
194 | } | |
195 | } | |
196 | enumerator->destroy(enumerator); | |
197 | return found; | |
198 | } | |
199 | ||
23f4e4b4 | 200 | METHOD(task_manager_t, retransmit, status_t, |
751bd02e | 201 | private_task_manager_t *this, u_int32_t message_seqnr) |
23f4e4b4 | 202 | { |
751bd02e CO |
203 | /* this.initiating packet used as marker for received response */ |
204 | if (message_seqnr == this->initiating.seqnr && this->initiating.packet ) | |
23f4e4b4 CO |
205 | { |
206 | u_int32_t timeout; | |
23f4e4b4 | 207 | packet_t *packet; |
da063ec9 | 208 | job_t *job; |
23f4e4b4 CO |
209 | |
210 | if (this->initiating.retransmitted <= this->retransmit_tries) | |
211 | { | |
212 | timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 * | |
213 | pow(this->retransmit_base, this->initiating.retransmitted)); | |
214 | } | |
215 | else | |
216 | { | |
217 | DBG1(DBG_IKE, "giving up after %d retransmits", | |
218 | this->initiating.retransmitted - 1); | |
219 | if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) | |
220 | { | |
221 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
222 | } | |
223 | return DESTROY_ME; | |
224 | } | |
225 | ||
226 | if (this->initiating.retransmitted) | |
227 | { | |
751bd02e CO |
228 | DBG1(DBG_IKE, "retransmit %d of request with message ID %d seqnr (%d)", |
229 | this->initiating.retransmitted, this->initiating.mid, message_seqnr); | |
23f4e4b4 CO |
230 | } |
231 | packet = this->initiating.packet->clone(this->initiating.packet); | |
232 | charon->sender->send(charon->sender, packet); | |
233 | ||
234 | this->initiating.retransmitted++; | |
751bd02e | 235 | job = (job_t*)retransmit_job_create(this->initiating.seqnr, |
23f4e4b4 CO |
236 | this->ike_sa->get_id(this->ike_sa)); |
237 | lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout); | |
238 | } | |
239 | return SUCCESS; | |
240 | } | |
241 | ||
4a09d9ee MW |
242 | METHOD(task_manager_t, initiate, status_t, |
243 | private_task_manager_t *this) | |
244 | { | |
26b55dc6 MW |
245 | enumerator_t *enumerator; |
246 | task_t *task; | |
247 | message_t *message; | |
248 | host_t *me, *other; | |
249 | status_t status; | |
73aaf76b | 250 | exchange_type_t exchange = EXCHANGE_TYPE_UNDEFINED; |
3e246c48 | 251 | bool new_mid = FALSE; |
751bd02e | 252 | bool expect_response = FALSE; |
73aaf76b MW |
253 | |
254 | if (!this->rng) | |
255 | { | |
256 | DBG1(DBG_IKE, "no RNG supported"); | |
257 | return FAILED; | |
258 | } | |
26b55dc6 MW |
259 | |
260 | if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED) | |
261 | { | |
262 | DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress", | |
263 | exchange_type_names, this->initiating.type); | |
264 | /* do not initiate if we already have a message in the air */ | |
265 | return SUCCESS; | |
266 | } | |
267 | ||
268 | if (this->active_tasks->get_count(this->active_tasks) == 0) | |
269 | { | |
270 | DBG2(DBG_IKE, "activating new tasks"); | |
271 | switch (this->ike_sa->get_state(this->ike_sa)) | |
272 | { | |
273 | case IKE_CREATED: | |
a2f8fc97 | 274 | activate_task(this, TASK_VENDOR_V1); |
0bcdb8e5 | 275 | activate_task(this, TASK_IKE_CERT_PRE_V1); |
a09972df | 276 | if (activate_task(this, TASK_MAIN_MODE)) |
26b55dc6 MW |
277 | { |
278 | exchange = ID_PROT; | |
c64a4b4f | 279 | activate_task(this, TASK_IKE_CERT_POST_V1); |
1cc4ec46 | 280 | activate_task(this, TASK_IKE_NATD_V1); |
26b55dc6 MW |
281 | } |
282 | break; | |
c5dc9d33 | 283 | case IKE_CONNECTING: |
69adeb5b | 284 | if (activate_task(this, TASK_XAUTH)) |
744c0801 | 285 | { |
c5dc9d33 | 286 | exchange = TRANSACTION; |
3e246c48 | 287 | new_mid = TRUE; |
744c0801 | 288 | } |
c5dc9d33 CO |
289 | break; |
290 | case IKE_ESTABLISHED: | |
156b8662 MW |
291 | if (activate_task(this, TASK_MODE_CONFIG)) |
292 | { | |
293 | exchange = TRANSACTION; | |
294 | new_mid = TRUE; | |
295 | break; | |
296 | } | |
c5dc9d33 | 297 | if (activate_task(this, TASK_QUICK_MODE)) |
23f4e4b4 | 298 | { |
c5dc9d33 | 299 | exchange = QUICK_MODE; |
3e246c48 MW |
300 | new_mid = TRUE; |
301 | break; | |
23f4e4b4 | 302 | } |
744c0801 | 303 | break; |
26b55dc6 MW |
304 | default: |
305 | break; | |
306 | } | |
307 | } | |
308 | else | |
309 | { | |
310 | DBG2(DBG_IKE, "reinitiating already active tasks"); | |
311 | enumerator = this->active_tasks->create_enumerator(this->active_tasks); | |
312 | while (enumerator->enumerate(enumerator, (void**)&task)) | |
313 | { | |
314 | DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task)); | |
315 | switch (task->get_type(task)) | |
316 | { | |
a09972df | 317 | case TASK_MAIN_MODE: |
26b55dc6 MW |
318 | exchange = ID_PROT; |
319 | break; | |
744c0801 MW |
320 | case TASK_QUICK_MODE: |
321 | exchange = QUICK_MODE; | |
322 | break; | |
69adeb5b | 323 | case TASK_XAUTH: |
52ac2ceb CO |
324 | exchange = TRANSACTION; |
325 | new_mid = TRUE; | |
326 | break; | |
26b55dc6 MW |
327 | default: |
328 | continue; | |
329 | } | |
330 | break; | |
331 | } | |
332 | enumerator->destroy(enumerator); | |
333 | } | |
334 | ||
73aaf76b | 335 | if (exchange == EXCHANGE_TYPE_UNDEFINED) |
26b55dc6 MW |
336 | { |
337 | DBG2(DBG_IKE, "nothing to initiate"); | |
338 | /* nothing to do yet... */ | |
339 | return SUCCESS; | |
340 | } | |
341 | ||
342 | me = this->ike_sa->get_my_host(this->ike_sa); | |
343 | other = this->ike_sa->get_other_host(this->ike_sa); | |
344 | ||
345 | message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION); | |
3e246c48 | 346 | if (new_mid) |
26b55dc6 | 347 | { |
73aaf76b MW |
348 | this->rng->get_bytes(this->rng, sizeof(this->initiating.mid), |
349 | (void*)&this->initiating.mid); | |
26b55dc6 | 350 | } |
3e246c48 | 351 | message->set_message_id(message, this->initiating.mid); |
26b55dc6 MW |
352 | message->set_source(message, me->clone(me)); |
353 | message->set_destination(message, other->clone(other)); | |
354 | message->set_exchange_type(message, exchange); | |
355 | this->initiating.type = exchange; | |
356 | this->initiating.retransmitted = 0; | |
357 | ||
358 | enumerator = this->active_tasks->create_enumerator(this->active_tasks); | |
359 | while (enumerator->enumerate(enumerator, (void*)&task)) | |
360 | { | |
361 | switch (task->build(task, message)) | |
362 | { | |
363 | case SUCCESS: | |
364 | /* task completed, remove it */ | |
365 | this->active_tasks->remove_at(this->active_tasks, enumerator); | |
366 | task->destroy(task); | |
367 | break; | |
368 | case NEED_MORE: | |
751bd02e | 369 | expect_response = TRUE; |
26b55dc6 MW |
370 | /* processed, but task needs another exchange */ |
371 | break; | |
372 | case FAILED: | |
373 | default: | |
374 | if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) | |
375 | { | |
376 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
377 | } | |
378 | /* FALL */ | |
379 | case DESTROY_ME: | |
380 | /* critical failure, destroy IKE_SA */ | |
381 | enumerator->destroy(enumerator); | |
382 | message->destroy(message); | |
383 | flush(this); | |
384 | return DESTROY_ME; | |
385 | } | |
386 | } | |
387 | enumerator->destroy(enumerator); | |
388 | ||
389 | /* update exchange type if a task changed it */ | |
390 | this->initiating.type = message->get_exchange_type(message); | |
751bd02e | 391 | this->initiating.seqnr++; |
26b55dc6 MW |
392 | |
393 | status = this->ike_sa->generate_message(this->ike_sa, message, | |
394 | &this->initiating.packet); | |
395 | if (status != SUCCESS) | |
396 | { | |
397 | /* message generation failed. There is nothing more to do than to | |
398 | * close the SA */ | |
399 | message->destroy(message); | |
400 | flush(this); | |
401 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
402 | return DESTROY_ME; | |
403 | } | |
404 | message->destroy(message); | |
405 | ||
751bd02e CO |
406 | if (expect_response) |
407 | { | |
408 | return retransmit(this, this->initiating.seqnr); | |
409 | } | |
156b8662 MW |
410 | charon->sender->send(charon->sender, |
411 | this->initiating.packet->clone(this->initiating.packet)); | |
412 | this->initiating.packet->destroy(this->initiating.packet); | |
413 | this->initiating.packet = NULL; | |
414 | return SUCCESS; | |
4a09d9ee MW |
415 | } |
416 | ||
417 | /** | |
418 | * handle exchange collisions | |
419 | */ | |
420 | static bool handle_collisions(private_task_manager_t *this, task_t *task) | |
421 | { | |
422 | return FALSE; | |
423 | } | |
424 | ||
425 | /** | |
426 | * build a response depending on the "passive" task list | |
427 | */ | |
428 | static status_t build_response(private_task_manager_t *this, message_t *request) | |
429 | { | |
430 | enumerator_t *enumerator; | |
431 | task_t *task; | |
432 | message_t *message; | |
433 | host_t *me, *other; | |
434 | bool delete = FALSE; | |
435 | status_t status; | |
436 | ||
437 | me = request->get_destination(request); | |
438 | other = request->get_source(request); | |
439 | ||
440 | message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION); | |
441 | message->set_exchange_type(message, request->get_exchange_type(request)); | |
442 | /* send response along the path the request came in */ | |
443 | message->set_source(message, me->clone(me)); | |
444 | message->set_destination(message, other->clone(other)); | |
73aaf76b | 445 | message->set_message_id(message, request->get_message_id(request)); |
4a09d9ee MW |
446 | message->set_request(message, FALSE); |
447 | ||
448 | enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); | |
449 | while (enumerator->enumerate(enumerator, (void*)&task)) | |
450 | { | |
451 | switch (task->build(task, message)) | |
452 | { | |
453 | case SUCCESS: | |
454 | /* task completed, remove it */ | |
455 | this->passive_tasks->remove_at(this->passive_tasks, enumerator); | |
456 | if (!handle_collisions(this, task)) | |
457 | { | |
458 | task->destroy(task); | |
459 | } | |
460 | break; | |
461 | case NEED_MORE: | |
462 | /* processed, but task needs another exchange */ | |
463 | if (handle_collisions(this, task)) | |
464 | { | |
465 | this->passive_tasks->remove_at(this->passive_tasks, | |
466 | enumerator); | |
467 | } | |
468 | break; | |
469 | case FAILED: | |
470 | default: | |
471 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
472 | /* FALL */ | |
473 | case DESTROY_ME: | |
474 | /* destroy IKE_SA, but SEND response first */ | |
475 | delete = TRUE; | |
476 | break; | |
477 | } | |
478 | if (delete) | |
479 | { | |
480 | break; | |
481 | } | |
482 | } | |
483 | enumerator->destroy(enumerator); | |
484 | ||
485 | /* message complete, send it */ | |
486 | DESTROY_IF(this->responding.packet); | |
487 | this->responding.packet = NULL; | |
488 | status = this->ike_sa->generate_message(this->ike_sa, message, | |
489 | &this->responding.packet); | |
490 | message->destroy(message); | |
491 | if (status != SUCCESS) | |
492 | { | |
493 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
494 | return DESTROY_ME; | |
495 | } | |
496 | ||
497 | charon->sender->send(charon->sender, | |
498 | this->responding.packet->clone(this->responding.packet)); | |
499 | if (delete) | |
500 | { | |
501 | return DESTROY_ME; | |
502 | } | |
503 | return SUCCESS; | |
504 | } | |
505 | ||
fbbd439b CO |
506 | /** |
507 | * Send a notify in a separate INFORMATIONAL exchange back to the sender. | |
e647c98a | 508 | * The notify protocol_id is set to ISAKMP |
fbbd439b CO |
509 | */ |
510 | static void send_notify_response(private_task_manager_t *this, | |
511 | message_t *request, notify_type_t type, | |
512 | chunk_t data, task_t *task) | |
513 | { | |
514 | message_t *response; | |
515 | packet_t *packet; | |
516 | host_t *me, *other; | |
517 | u_int32_t mid; | |
518 | ||
519 | if (request && request->get_exchange_type(request) == INFORMATIONAL_V1) | |
520 | { /* don't respond to INFORMATIONAL requests to avoid a notify war */ | |
521 | DBG1(DBG_IKE, "ignore malformed INFORMATIONAL request"); | |
522 | return; | |
523 | } | |
524 | ||
525 | response = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION); | |
526 | response->set_exchange_type(response, INFORMATIONAL_V1); | |
527 | response->set_request(response, TRUE); | |
528 | this->rng->get_bytes(this->rng, sizeof(mid), (void*)&mid); | |
529 | response->set_message_id(response, mid); | |
530 | ||
531 | if (task) | |
532 | { | |
533 | /* Let the task build the response */ | |
534 | if (task->build(task,response) != SUCCESS) | |
535 | { | |
536 | response->destroy(response); | |
537 | return; | |
538 | } | |
539 | } | |
540 | else | |
541 | { | |
e647c98a CO |
542 | notify_payload_t *notify; |
543 | ||
544 | notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1, | |
545 | PROTO_IKE, type); | |
546 | ||
547 | if (data.ptr) | |
548 | { | |
549 | notify->set_notification_data(notify, data); | |
550 | } | |
551 | ||
552 | response->add_payload(response, (payload_t*)notify); | |
fbbd439b CO |
553 | } |
554 | ||
555 | me = this->ike_sa->get_my_host(this->ike_sa); | |
556 | if (me->is_anyaddr(me)) | |
557 | { | |
558 | me = request->get_destination(request); | |
559 | this->ike_sa->set_my_host(this->ike_sa, me->clone(me)); | |
560 | } | |
561 | other = this->ike_sa->get_other_host(this->ike_sa); | |
562 | if (other->is_anyaddr(other)) | |
563 | { | |
564 | other = request->get_source(request); | |
565 | this->ike_sa->set_other_host(this->ike_sa, other->clone(other)); | |
566 | } | |
567 | response->set_source(response, me->clone(me)); | |
568 | response->set_destination(response, other->clone(other)); | |
569 | if (this->ike_sa->generate_message(this->ike_sa, response, | |
570 | &packet) == SUCCESS) | |
571 | { | |
572 | charon->sender->send(charon->sender, packet); | |
573 | } | |
574 | response->destroy(response); | |
575 | } | |
576 | ||
4a09d9ee MW |
577 | /** |
578 | * handle an incoming request message | |
579 | */ | |
580 | static status_t process_request(private_task_manager_t *this, | |
581 | message_t *message) | |
582 | { | |
583 | enumerator_t *enumerator; | |
584 | task_t *task = NULL; | |
8cb6f4f9 | 585 | bool send_response = FALSE; |
07abb470 CO |
586 | payload_t *payload; |
587 | notify_payload_t *notify; | |
4a09d9ee MW |
588 | |
589 | if (this->passive_tasks->get_count(this->passive_tasks) == 0) | |
590 | { /* create tasks depending on request type, if not already some queued */ | |
591 | switch (message->get_exchange_type(message)) | |
592 | { | |
593 | case ID_PROT: | |
a2f8fc97 | 594 | task = (task_t *)ike_vendor_v1_create(this->ike_sa, FALSE); |
01685247 | 595 | this->passive_tasks->insert_last(this->passive_tasks, task); |
0bcdb8e5 | 596 | task = (task_t*)ike_cert_pre_v1_create(this->ike_sa, FALSE); |
8ad5cd1f | 597 | this->passive_tasks->insert_last(this->passive_tasks, task); |
c73c832c MW |
598 | task = (task_t *)main_mode_create(this->ike_sa, FALSE); |
599 | this->passive_tasks->insert_last(this->passive_tasks, task); | |
c64a4b4f MW |
600 | task = (task_t*)ike_cert_post_v1_create(this->ike_sa, FALSE); |
601 | this->passive_tasks->insert_last(this->passive_tasks, task); | |
1cc4ec46 TB |
602 | task = (task_t *)ike_natd_v1_create(this->ike_sa, FALSE); |
603 | this->passive_tasks->insert_last(this->passive_tasks, task); | |
4a09d9ee MW |
604 | break; |
605 | case AGGRESSIVE: | |
606 | /* TODO-IKEv1: agressive mode */ | |
607 | return FAILED; | |
608 | case QUICK_MODE: | |
a22b9e4f MW |
609 | if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) |
610 | { | |
611 | DBG1(DBG_IKE, "received quick mode request for " | |
612 | "unestablished IKE_SA, ignored"); | |
613 | return FAILED; | |
614 | } | |
744c0801 MW |
615 | task = (task_t *)quick_mode_create(this->ike_sa, NULL, |
616 | NULL, NULL); | |
617 | this->passive_tasks->insert_last(this->passive_tasks, task); | |
618 | break; | |
4a09d9ee | 619 | case INFORMATIONAL_V1: |
07abb470 CO |
620 | enumerator = message->create_payload_enumerator(message); |
621 | while (enumerator->enumerate(enumerator, &payload)) | |
622 | { | |
623 | switch (payload->get_type(payload)) | |
624 | { | |
625 | case NOTIFY_V1: | |
626 | { | |
627 | notify = (notify_payload_t*)payload; | |
628 | switch (notify->get_notify_type(notify)) | |
629 | { | |
630 | /* TODO-IKEv1: Add notification types here as needed */ | |
631 | case INITIAL_CONTACT_IKEV1: | |
632 | break; | |
633 | default: | |
634 | if(notify->get_notify_type(notify) < 16384) | |
635 | { | |
636 | DBG1(DBG_IKE, "Received %N error notification.", notify_type_names, notify->get_notify_type(notify)); | |
0793ac49 | 637 | enumerator->destroy(enumerator); |
07abb470 CO |
638 | return FAILED; |
639 | } | |
640 | break; | |
641 | } | |
642 | break; | |
643 | } | |
644 | case DELETE_V1: | |
645 | { | |
5d1eeec2 CO |
646 | delete_payload_t *delete; |
647 | delete = (delete_payload_t*)payload; | |
648 | ||
649 | if (delete->get_protocol_id(delete) == PROTO_IKE) | |
650 | { | |
651 | task = (task_t*)ike_delete_create(this->ike_sa, | |
652 | FALSE); | |
653 | } | |
654 | else | |
655 | { | |
656 | task = (task_t*)child_delete_create(this->ike_sa, | |
657 | PROTO_NONE, 0); | |
658 | } | |
07abb470 CO |
659 | break; |
660 | } | |
661 | default: | |
662 | break; | |
663 | } | |
664 | if (task) | |
665 | { | |
5d1eeec2 | 666 | this->passive_tasks->insert_last(this->passive_tasks, task); |
07abb470 CO |
667 | } |
668 | } | |
669 | enumerator->destroy(enumerator); | |
670 | break; | |
c5dc9d33 | 671 | case TRANSACTION: |
156b8662 MW |
672 | if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) |
673 | { | |
674 | task = (task_t *)mode_config_create(this->ike_sa, FALSE); | |
675 | } | |
676 | else | |
677 | { | |
678 | task = (task_t *)xauth_create(this->ike_sa, FALSE); | |
679 | } | |
c5dc9d33 CO |
680 | this->passive_tasks->insert_last(this->passive_tasks, task); |
681 | break; | |
4a09d9ee MW |
682 | default: |
683 | return FAILED; | |
684 | } | |
685 | } | |
686 | /* let the tasks process the message */ | |
687 | enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); | |
688 | while (enumerator->enumerate(enumerator, (void*)&task)) | |
689 | { | |
690 | switch (task->process(task, message)) | |
691 | { | |
692 | case SUCCESS: | |
693 | /* task completed, remove it */ | |
694 | this->passive_tasks->remove_at(this->passive_tasks, enumerator); | |
695 | task->destroy(task); | |
8cb6f4f9 | 696 | break; |
4a09d9ee MW |
697 | case NEED_MORE: |
698 | /* processed, but task needs at least another call to build() */ | |
8cb6f4f9 | 699 | send_response = TRUE; |
4a09d9ee | 700 | break; |
fbbd439b CO |
701 | case FAILED_SEND_ERROR: |
702 | send_notify_response(this, NULL, 0, chunk_empty, task); | |
4a09d9ee MW |
703 | case FAILED: |
704 | default: | |
705 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
706 | /* FALL */ | |
707 | case DESTROY_ME: | |
708 | /* critical failure, destroy IKE_SA */ | |
709 | this->passive_tasks->remove_at(this->passive_tasks, enumerator); | |
710 | enumerator->destroy(enumerator); | |
711 | task->destroy(task); | |
712 | return DESTROY_ME; | |
713 | } | |
714 | } | |
715 | enumerator->destroy(enumerator); | |
716 | ||
8cb6f4f9 TB |
717 | if (send_response) |
718 | { | |
69adeb5b MW |
719 | if (build_response(this, message) != SUCCESS) |
720 | { | |
721 | return DESTROY_ME; | |
722 | } | |
723 | } | |
724 | if (this->passive_tasks->get_count(this->passive_tasks) == 0 && | |
725 | this->queued_tasks->get_count(this->queued_tasks) > 0) | |
726 | { | |
727 | /* passive tasks completed, check if an active task has been queued, | |
728 | * such as XAUTH or modeconfig push */ | |
729 | return initiate(this); | |
8cb6f4f9 TB |
730 | } |
731 | return SUCCESS; | |
4a09d9ee MW |
732 | } |
733 | ||
26b55dc6 MW |
734 | /** |
735 | * handle an incoming response message | |
736 | */ | |
737 | static status_t process_response(private_task_manager_t *this, | |
738 | message_t *message) | |
739 | { | |
740 | enumerator_t *enumerator; | |
741 | task_t *task; | |
742 | ||
743 | if (message->get_exchange_type(message) != this->initiating.type) | |
744 | { | |
745 | DBG1(DBG_IKE, "received %N response, but expected %N", | |
746 | exchange_type_names, message->get_exchange_type(message), | |
747 | exchange_type_names, this->initiating.type); | |
748 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
749 | return DESTROY_ME; | |
750 | } | |
751 | ||
752 | enumerator = this->active_tasks->create_enumerator(this->active_tasks); | |
753 | while (enumerator->enumerate(enumerator, (void*)&task)) | |
754 | { | |
755 | switch (task->process(task, message)) | |
756 | { | |
757 | case SUCCESS: | |
758 | /* task completed, remove it */ | |
759 | this->active_tasks->remove_at(this->active_tasks, enumerator); | |
760 | task->destroy(task); | |
761 | break; | |
762 | case NEED_MORE: | |
763 | /* processed, but task needs another exchange */ | |
764 | break; | |
765 | case FAILED: | |
766 | default: | |
767 | charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); | |
768 | /* FALL */ | |
769 | case DESTROY_ME: | |
770 | /* critical failure, destroy IKE_SA */ | |
771 | this->active_tasks->remove_at(this->active_tasks, enumerator); | |
772 | enumerator->destroy(enumerator); | |
773 | task->destroy(task); | |
774 | return DESTROY_ME; | |
775 | } | |
776 | } | |
777 | enumerator->destroy(enumerator); | |
778 | ||
779 | this->initiating.type = EXCHANGE_TYPE_UNDEFINED; | |
780 | this->initiating.packet->destroy(this->initiating.packet); | |
781 | this->initiating.packet = NULL; | |
782 | ||
783 | return initiate(this); | |
784 | } | |
785 | ||
b235e69c TB |
786 | /** |
787 | * Parse the given message and verify that it is valid. | |
788 | */ | |
789 | static status_t parse_message(private_task_manager_t *this, message_t *msg) | |
790 | { | |
791 | status_t status; | |
792 | ||
793 | status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa)); | |
794 | ||
795 | if (status != SUCCESS) | |
796 | { | |
797 | switch (status) | |
798 | { | |
29a5e070 TB |
799 | case NOT_SUPPORTED: |
800 | DBG1(DBG_IKE, "unsupported exchange type"); | |
801 | send_notify_response(this, msg, | |
fbbd439b | 802 | INVALID_EXCHANGE_TYPE, chunk_empty, NULL); |
29a5e070 | 803 | break; |
b235e69c TB |
804 | case PARSE_ERROR: |
805 | DBG1(DBG_IKE, "message parsing failed"); | |
806 | send_notify_response(this, msg, | |
fbbd439b | 807 | PAYLOAD_MALFORMED, chunk_empty, NULL); |
b235e69c TB |
808 | break; |
809 | case VERIFY_ERROR: | |
810 | DBG1(DBG_IKE, "message verification failed"); | |
811 | send_notify_response(this, msg, | |
fbbd439b | 812 | PAYLOAD_MALFORMED, chunk_empty, NULL); |
b235e69c TB |
813 | break; |
814 | case FAILED: | |
815 | DBG1(DBG_IKE, "integrity check failed"); | |
816 | send_notify_response(this, msg, | |
fbbd439b | 817 | INVALID_HASH_INFORMATION, chunk_empty, NULL); |
b235e69c TB |
818 | break; |
819 | case INVALID_STATE: | |
820 | DBG1(DBG_IKE, "found encrypted message, but no keys available"); | |
821 | send_notify_response(this, msg, | |
fbbd439b | 822 | PAYLOAD_MALFORMED, chunk_empty, NULL); |
b235e69c TB |
823 | default: |
824 | break; | |
825 | } | |
826 | DBG1(DBG_IKE, "%N %s with message ID %d processing failed", | |
827 | exchange_type_names, msg->get_exchange_type(msg), | |
828 | msg->get_request(msg) ? "request" : "response", | |
829 | msg->get_message_id(msg)); | |
830 | ||
831 | if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED) | |
832 | { /* invalid initiation attempt, close SA */ | |
833 | return DESTROY_ME; | |
834 | } | |
835 | } | |
836 | return status; | |
837 | } | |
838 | ||
4a09d9ee MW |
839 | METHOD(task_manager_t, process_message, status_t, |
840 | private_task_manager_t *this, message_t *msg) | |
841 | { | |
73aaf76b | 842 | u_int32_t hash, mid; |
3d59c5c3 | 843 | host_t *me, *other; |
68c6863b | 844 | status_t status; |
73aaf76b | 845 | |
1960312c | 846 | /* TODO-IKEv1: update hosts more selectively */ |
3d59c5c3 TB |
847 | me = msg->get_destination(msg); |
848 | other = msg->get_source(msg); | |
68c6863b | 849 | mid = msg->get_message_id(msg); |
1960312c | 850 | |
73aaf76b MW |
851 | if ((mid && mid == this->initiating.mid) || |
852 | (this->initiating.mid == 0 && | |
853 | this->active_tasks->get_count(this->active_tasks))) | |
4a09d9ee | 854 | { |
7519106d | 855 | msg->set_request(msg, FALSE); |
b235e69c | 856 | status = parse_message(this, msg); |
1960312c TB |
857 | if (status != SUCCESS) |
858 | { | |
859 | return status; | |
860 | } | |
861 | this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, | |
862 | time_monotonic(NULL)); | |
3d59c5c3 | 863 | this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE); |
73aaf76b MW |
864 | charon->bus->message(charon->bus, msg, FALSE); |
865 | if (process_response(this, msg) != SUCCESS) | |
4a09d9ee MW |
866 | { |
867 | flush(this); | |
868 | return DESTROY_ME; | |
869 | } | |
870 | } | |
871 | else | |
872 | { | |
9cc8bd4f | 873 | hash = chunk_hash(msg->get_packet_data(msg)); |
98628528 | 874 | if (hash == this->responding.hash && this->responding.packet) |
73aaf76b MW |
875 | { |
876 | DBG1(DBG_IKE, "received retransmit of request with ID %d, " | |
877 | "retransmitting response", mid); | |
878 | charon->sender->send(charon->sender, | |
98628528 | 879 | this->responding.packet->clone(this->responding.packet)); |
73aaf76b MW |
880 | return SUCCESS; |
881 | } | |
7519106d | 882 | msg->set_request(msg, TRUE); |
b235e69c | 883 | status = parse_message(this, msg); |
1960312c TB |
884 | if (status != SUCCESS) |
885 | { | |
886 | return status; | |
887 | } | |
888 | /* if this IKE_SA is virgin, we check for a config */ | |
889 | if (this->ike_sa->get_ike_cfg(this->ike_sa) == NULL) | |
890 | { | |
891 | ike_sa_id_t *ike_sa_id; | |
892 | ike_cfg_t *ike_cfg; | |
893 | job_t *job; | |
894 | ike_cfg = charon->backends->get_ike_cfg(charon->backends, me, other); | |
895 | if (ike_cfg == NULL) | |
896 | { | |
897 | /* no config found for these hosts, destroy */ | |
4cfd0db8 TB |
898 | DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N", |
899 | me, other, notify_type_names, NO_PROPOSAL_CHOSEN); | |
900 | send_notify_response(this, msg, | |
fbbd439b | 901 | NO_PROPOSAL_CHOSEN, chunk_empty, NULL); |
1960312c TB |
902 | return DESTROY_ME; |
903 | } | |
904 | this->ike_sa->set_ike_cfg(this->ike_sa, ike_cfg); | |
905 | ike_cfg->destroy(ike_cfg); | |
906 | /* add a timeout if peer does not establish it completely */ | |
907 | ike_sa_id = this->ike_sa->get_id(this->ike_sa); | |
908 | job = (job_t*)delete_ike_sa_job_create(ike_sa_id, FALSE); | |
909 | lib->scheduler->schedule_job(lib->scheduler, job, | |
910 | lib->settings->get_int(lib->settings, | |
911 | "charon.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT)); | |
912 | } | |
913 | this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, | |
914 | time_monotonic(NULL)); | |
3d59c5c3 | 915 | this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE); |
73aaf76b MW |
916 | charon->bus->message(charon->bus, msg, TRUE); |
917 | if (process_request(this, msg) != SUCCESS) | |
26b55dc6 MW |
918 | { |
919 | flush(this); | |
920 | return DESTROY_ME; | |
921 | } | |
73aaf76b | 922 | |
73aaf76b | 923 | this->responding.mid = mid; |
9cc8bd4f | 924 | this->responding.hash = hash; |
4a09d9ee MW |
925 | } |
926 | return SUCCESS; | |
927 | } | |
928 | ||
929 | METHOD(task_manager_t, queue_task, void, | |
930 | private_task_manager_t *this, task_t *task) | |
931 | { | |
932 | DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task)); | |
933 | this->queued_tasks->insert_last(this->queued_tasks, task); | |
934 | } | |
935 | ||
936 | METHOD(task_manager_t, adopt_tasks, void, | |
937 | private_task_manager_t *this, task_manager_t *other_public) | |
938 | { | |
939 | private_task_manager_t *other = (private_task_manager_t*)other_public; | |
940 | task_t *task; | |
941 | ||
942 | /* move queued tasks from other to this */ | |
943 | while (other->queued_tasks->remove_last(other->queued_tasks, | |
944 | (void**)&task) == SUCCESS) | |
945 | { | |
946 | DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); | |
947 | task->migrate(task, this->ike_sa); | |
948 | this->queued_tasks->insert_first(this->queued_tasks, task); | |
949 | } | |
950 | } | |
951 | ||
952 | METHOD(task_manager_t, busy, bool, | |
953 | private_task_manager_t *this) | |
954 | { | |
955 | return (this->active_tasks->get_count(this->active_tasks) > 0); | |
956 | } | |
957 | ||
958 | METHOD(task_manager_t, incr_mid, void, | |
959 | private_task_manager_t *this, bool initiate) | |
960 | { | |
4a09d9ee MW |
961 | } |
962 | ||
963 | METHOD(task_manager_t, reset, void, | |
964 | private_task_manager_t *this, u_int32_t initiate, u_int32_t respond) | |
965 | { | |
4a09d9ee MW |
966 | } |
967 | ||
968 | METHOD(task_manager_t, create_task_enumerator, enumerator_t*, | |
969 | private_task_manager_t *this, task_queue_t queue) | |
970 | { | |
971 | switch (queue) | |
972 | { | |
973 | case TASK_QUEUE_ACTIVE: | |
974 | return this->active_tasks->create_enumerator(this->active_tasks); | |
975 | case TASK_QUEUE_PASSIVE: | |
976 | return this->passive_tasks->create_enumerator(this->passive_tasks); | |
977 | case TASK_QUEUE_QUEUED: | |
978 | return this->queued_tasks->create_enumerator(this->queued_tasks); | |
979 | default: | |
980 | return enumerator_create_empty(); | |
981 | } | |
982 | } | |
983 | ||
984 | METHOD(task_manager_t, destroy, void, | |
985 | private_task_manager_t *this) | |
986 | { | |
987 | flush(this); | |
988 | ||
989 | this->active_tasks->destroy(this->active_tasks); | |
990 | this->queued_tasks->destroy(this->queued_tasks); | |
991 | this->passive_tasks->destroy(this->passive_tasks); | |
992 | ||
993 | DESTROY_IF(this->responding.packet); | |
994 | DESTROY_IF(this->initiating.packet); | |
73aaf76b | 995 | DESTROY_IF(this->rng); |
4a09d9ee MW |
996 | free(this); |
997 | } | |
998 | ||
999 | /* | |
1000 | * see header file | |
1001 | */ | |
1002 | task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) | |
1003 | { | |
1004 | private_task_manager_t *this; | |
1005 | ||
1006 | INIT(this, | |
1007 | .public = { | |
1008 | .task_manager = { | |
1009 | .process_message = _process_message, | |
1010 | .queue_task = _queue_task, | |
1011 | .initiate = _initiate, | |
1012 | .retransmit = _retransmit, | |
1013 | .incr_mid = _incr_mid, | |
1014 | .reset = _reset, | |
1015 | .adopt_tasks = _adopt_tasks, | |
1016 | .busy = _busy, | |
1017 | .create_task_enumerator = _create_task_enumerator, | |
1018 | .destroy = _destroy, | |
1019 | }, | |
1020 | }, | |
1021 | .ike_sa = ike_sa, | |
1022 | .initiating.type = EXCHANGE_TYPE_UNDEFINED, | |
73aaf76b | 1023 | .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), |
4a09d9ee MW |
1024 | .queued_tasks = linked_list_create(), |
1025 | .active_tasks = linked_list_create(), | |
1026 | .passive_tasks = linked_list_create(), | |
1027 | .retransmit_tries = lib->settings->get_int(lib->settings, | |
1028 | "charon.retransmit_tries", RETRANSMIT_TRIES), | |
1029 | .retransmit_timeout = lib->settings->get_double(lib->settings, | |
1030 | "charon.retransmit_timeout", RETRANSMIT_TIMEOUT), | |
1031 | .retransmit_base = lib->settings->get_double(lib->settings, | |
1032 | "charon.retransmit_base", RETRANSMIT_BASE), | |
1033 | ); | |
1034 | ||
1035 | return &this->public; | |
1036 | } |