2 * Copyright (C) 2005-2007 Martin Willi
3 * Copyright (C) 2005 Jan Hutter
4 * Hochschule fuer Technik Rapperswil
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>.
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
17 #include "child_rekey.h"
20 #include <encoding/payloads/notify_payload.h>
21 #include <sa/ikev2/tasks/child_create.h>
22 #include <sa/ikev2/tasks/child_delete.h>
23 #include <processing/jobs/rekey_child_sa_job.h>
24 #include <processing/jobs/rekey_ike_sa_job.h>
27 typedef struct private_child_rekey_t private_child_rekey_t
;
30 * Private members of a child_rekey_t task.
32 struct private_child_rekey_t
{
35 * Public methods and task_t interface.
45 * Are we the initiator?
50 * Protocol of CHILD_SA to rekey
52 protocol_id_t protocol
;
55 * Inbound SPI of CHILD_SA to rekey
60 * the CHILD_CREATE task which is reused to simplify rekeying
62 child_create_t
*child_create
;
65 * the CHILD_DELETE task to delete rekeyed CHILD_SA
67 child_delete_t
*child_delete
;
70 * CHILD_SA which gets rekeyed
75 * colliding task, may be delete or rekey
80 * Indicate that peer destroyed the redundant child from collision.
81 * This happens if a peer's delete notification for the redundant
82 * child gets processed before the rekey job. If so, we must not
83 * touch the child created in the collision since it points to
84 * memory already freed.
86 bool other_child_destroyed
;
90 * Schedule a retry if rekeying temporary failed
92 static void schedule_delayed_rekey(private_child_rekey_t
*this)
97 retry
= RETRY_INTERVAL
- (random() % RETRY_JITTER
);
98 job
= (job_t
*)rekey_child_sa_job_create(
99 this->child_sa
->get_protocol(this->child_sa
),
100 this->child_sa
->get_spi(this->child_sa
, TRUE
),
101 this->ike_sa
->get_my_host(this->ike_sa
));
102 DBG1(DBG_IKE
, "CHILD_SA rekeying failed, trying again in %d seconds", retry
);
103 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
104 lib
->scheduler
->schedule_job(lib
->scheduler
, job
, retry
);
108 * Implementation of task_t.build for initiator, after rekeying
110 static status_t
build_i_delete(private_child_rekey_t
*this, message_t
*message
)
112 /* update exchange type to INFORMATIONAL for the delete */
113 message
->set_exchange_type(message
, INFORMATIONAL
);
115 return this->child_delete
->task
.build(&this->child_delete
->task
, message
);
119 * Implementation of task_t.process for initiator, after rekeying
121 static status_t
process_i_delete(private_child_rekey_t
*this, message_t
*message
)
123 return this->child_delete
->task
.process(&this->child_delete
->task
, message
);
127 * find a child using the REKEY_SA notify
129 static void find_child(private_child_rekey_t
*this, message_t
*message
)
131 notify_payload_t
*notify
;
132 protocol_id_t protocol
;
135 notify
= message
->get_notify(message
, REKEY_SA
);
138 protocol
= notify
->get_protocol_id(notify
);
139 spi
= notify
->get_spi(notify
);
141 if (protocol
== PROTO_ESP
|| protocol
== PROTO_AH
)
143 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, protocol
,
149 METHOD(task_t
, build_i
, status_t
,
150 private_child_rekey_t
*this, message_t
*message
)
152 notify_payload_t
*notify
;
156 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
159 { /* check if it is an outbound CHILD_SA */
160 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
163 { /* CHILD_SA is gone, unable to rekey. As an empty CREATE_CHILD_SA
164 * exchange is invalid, we fall back to an INFORMATIONAL exchange.*/
165 message
->set_exchange_type(message
, INFORMATIONAL
);
168 /* we work only with the inbound SPI */
169 this->spi
= this->child_sa
->get_spi(this->child_sa
, TRUE
);
171 config
= this->child_sa
->get_config(this->child_sa
);
174 /* our CHILD_CREATE task does the hard work for us */
175 if (!this->child_create
)
177 this->child_create
= child_create_create(this->ike_sa
,
178 config
->get_ref(config
), TRUE
, NULL
, NULL
);
180 reqid
= this->child_sa
->get_reqid(this->child_sa
);
181 this->child_create
->use_reqid(this->child_create
, reqid
);
182 this->child_create
->use_marks(this->child_create
,
183 this->child_sa
->get_mark(this->child_sa
, TRUE
).value
,
184 this->child_sa
->get_mark(this->child_sa
, FALSE
).value
);
186 if (this->child_create
->task
.build(&this->child_create
->task
,
187 message
) != NEED_MORE
)
189 schedule_delayed_rekey(this);
192 if (message
->get_exchange_type(message
) == CREATE_CHILD_SA
)
194 /* don't add the notify if the CHILD_CREATE task changed the exchange */
195 notify
= notify_payload_create_from_protocol_and_type(PLV2_NOTIFY
,
196 this->protocol
, REKEY_SA
);
197 notify
->set_spi(notify
, this->spi
);
198 message
->add_payload(message
, (payload_t
*)notify
);
200 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
205 METHOD(task_t
, process_r
, status_t
,
206 private_child_rekey_t
*this, message_t
*message
)
208 /* let the CHILD_CREATE task process the message */
209 this->child_create
->task
.process(&this->child_create
->task
, message
);
211 find_child(this, message
);
216 METHOD(task_t
, build_r
, status_t
,
217 private_child_rekey_t
*this, message_t
*message
)
222 if (this->child_sa
== NULL
||
223 this->child_sa
->get_state(this->child_sa
) == CHILD_DELETING
)
225 DBG1(DBG_IKE
, "unable to rekey, CHILD_SA not found");
226 message
->add_notify(message
, TRUE
, NO_PROPOSAL_CHOSEN
, chunk_empty
);
230 /* let the CHILD_CREATE task build the response */
231 reqid
= this->child_sa
->get_reqid(this->child_sa
);
232 this->child_create
->use_reqid(this->child_create
, reqid
);
233 this->child_create
->use_marks(this->child_create
,
234 this->child_sa
->get_mark(this->child_sa
, TRUE
).value
,
235 this->child_sa
->get_mark(this->child_sa
, FALSE
).value
);
236 config
= this->child_sa
->get_config(this->child_sa
);
237 this->child_create
->set_config(this->child_create
, config
->get_ref(config
));
238 this->child_create
->task
.build(&this->child_create
->task
, message
);
240 if (message
->get_payload(message
, PLV2_SECURITY_ASSOCIATION
) == NULL
)
242 /* rekeying failed, reuse old child */
243 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
247 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
249 /* invoke rekey hook */
250 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
251 this->child_create
->get_child(this->child_create
));
256 * Handle a rekey collision
258 static child_sa_t
*handle_collision(private_child_rekey_t
*this)
260 child_sa_t
*to_delete
;
262 if (this->collision
->get_type(this->collision
) == TASK_CHILD_REKEY
)
264 chunk_t this_nonce
, other_nonce
;
265 private_child_rekey_t
*other
= (private_child_rekey_t
*)this->collision
;
267 this_nonce
= this->child_create
->get_lower_nonce(this->child_create
);
268 other_nonce
= other
->child_create
->get_lower_nonce(other
->child_create
);
270 /* if we have the lower nonce, delete rekeyed SA. If not, delete
272 if (memcmp(this_nonce
.ptr
, other_nonce
.ptr
,
273 min(this_nonce
.len
, other_nonce
.len
)) > 0)
275 child_sa_t
*child_sa
;
277 DBG1(DBG_IKE
, "CHILD_SA rekey collision won, deleting old child");
278 to_delete
= this->child_sa
;
279 /* don't touch child other created, it has already been deleted */
280 if (!this->other_child_destroyed
)
282 /* disable close action and updown event for redundant child */
283 child_sa
= other
->child_create
->get_child(other
->child_create
);
286 child_sa
->set_close_action(child_sa
, ACTION_NONE
);
287 if (child_sa
->get_state(child_sa
) != CHILD_REKEYING
)
289 child_sa
->set_state(child_sa
, CHILD_REKEYING
);
296 DBG1(DBG_IKE
, "CHILD_SA rekey collision lost, "
297 "deleting rekeyed child");
298 to_delete
= this->child_create
->get_child(this->child_create
);
303 child_delete_t
*del
= (child_delete_t
*)this->collision
;
305 /* we didn't had a chance to compare the nonces, so we delete
306 * the CHILD_SA the other is not deleting. */
307 if (del
->get_child(del
) != this->child_sa
)
309 DBG1(DBG_IKE
, "CHILD_SA rekey/delete collision, "
310 "deleting rekeyed child");
311 to_delete
= this->child_sa
;
315 DBG1(DBG_IKE
, "CHILD_SA rekey/delete collision, "
316 "deleting redundant child");
317 to_delete
= this->child_create
->get_child(this->child_create
);
323 METHOD(task_t
, process_i
, status_t
,
324 private_child_rekey_t
*this, message_t
*message
)
326 protocol_id_t protocol
;
328 child_sa_t
*to_delete
;
330 if (message
->get_notify(message
, NO_ADDITIONAL_SAS
))
332 DBG1(DBG_IKE
, "peer seems to not support CHILD_SA rekeying, "
333 "starting reauthentication");
334 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
335 lib
->processor
->queue_job(lib
->processor
,
336 (job_t
*)rekey_ike_sa_job_create(
337 this->ike_sa
->get_id(this->ike_sa
), TRUE
));
341 if (this->child_create
->task
.process(&this->child_create
->task
,
342 message
) == NEED_MORE
)
344 /* bad DH group while rekeying, retry, or failure requiring deletion */
347 if (message
->get_payload(message
, PLV2_SECURITY_ASSOCIATION
) == NULL
)
349 /* establishing new child failed, reuse old. but not when we
350 * received a delete in the meantime */
351 if (!(this->collision
&&
352 this->collision
->get_type(this->collision
) == TASK_CHILD_DELETE
))
354 schedule_delayed_rekey(this);
359 /* check for rekey collisions */
362 to_delete
= handle_collision(this);
366 to_delete
= this->child_sa
;
369 if (to_delete
!= this->child_create
->get_child(this->child_create
))
370 { /* invoke rekey hook if rekeying successful */
371 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
372 this->child_create
->get_child(this->child_create
));
375 if (to_delete
== NULL
)
379 /* disable updown event for redundant CHILD_SA */
380 if (to_delete
->get_state(to_delete
) != CHILD_REKEYING
)
382 to_delete
->set_state(to_delete
, CHILD_REKEYING
);
384 spi
= to_delete
->get_spi(to_delete
, TRUE
);
385 protocol
= to_delete
->get_protocol(to_delete
);
387 /* rekeying done, delete the obsolete CHILD_SA using a subtask */
388 this->child_delete
= child_delete_create(this->ike_sa
, protocol
, spi
, FALSE
);
389 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_i_delete
;
390 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_i_delete
;
395 METHOD(task_t
, get_type
, task_type_t
,
396 private_child_rekey_t
*this)
398 return TASK_CHILD_REKEY
;
401 METHOD(child_rekey_t
, collide
, void,
402 private_child_rekey_t
*this, task_t
*other
)
404 /* the task manager only detects exchange collision, but not if
405 * the collision is for the same child. we check it here. */
406 if (other
->get_type(other
) == TASK_CHILD_REKEY
)
408 private_child_rekey_t
*rekey
= (private_child_rekey_t
*)other
;
409 if (rekey
->child_sa
!= this->child_sa
)
411 /* not the same child => no collision */
412 other
->destroy(other
);
416 else if (other
->get_type(other
) == TASK_CHILD_DELETE
)
418 child_delete_t
*del
= (child_delete_t
*)other
;
419 if (this->collision
&&
420 this->collision
->get_type(this->collision
) == TASK_CHILD_REKEY
)
422 private_child_rekey_t
*rekey
;
424 rekey
= (private_child_rekey_t
*)this->collision
;
425 if (del
->get_child(del
) == rekey
->child_create
->get_child(rekey
->child_create
))
427 /* peer deletes redundant child created in collision */
428 this->other_child_destroyed
= TRUE
;
429 other
->destroy(other
);
433 if (del
->get_child(del
) != this->child_sa
)
435 /* not the same child => no collision */
436 other
->destroy(other
);
442 /* any other task is not critical for collisisions, ignore */
443 other
->destroy(other
);
446 DBG1(DBG_IKE
, "detected %N collision with %N", task_type_names
,
447 TASK_CHILD_REKEY
, task_type_names
, other
->get_type(other
));
448 DESTROY_IF(this->collision
);
449 this->collision
= other
;
452 METHOD(task_t
, migrate
, void,
453 private_child_rekey_t
*this, ike_sa_t
*ike_sa
)
455 if (this->child_create
)
457 this->child_create
->task
.migrate(&this->child_create
->task
, ike_sa
);
459 if (this->child_delete
)
461 this->child_delete
->task
.migrate(&this->child_delete
->task
, ike_sa
);
463 DESTROY_IF(this->collision
);
465 this->ike_sa
= ike_sa
;
466 this->collision
= NULL
;
469 METHOD(task_t
, destroy
, void,
470 private_child_rekey_t
*this)
472 if (this->child_create
)
474 this->child_create
->task
.destroy(&this->child_create
->task
);
476 if (this->child_delete
)
478 this->child_delete
->task
.destroy(&this->child_delete
->task
);
480 DESTROY_IF(this->collision
);
485 * Described in header.
487 child_rekey_t
*child_rekey_create(ike_sa_t
*ike_sa
, protocol_id_t protocol
,
490 private_child_rekey_t
*this;
495 .get_type
= _get_type
,
502 .protocol
= protocol
,
506 if (protocol
!= PROTO_NONE
)
508 this->public.task
.build
= _build_i
;
509 this->public.task
.process
= _process_i
;
510 this->initiator
= TRUE
;
511 this->child_create
= NULL
;
515 this->public.task
.build
= _build_r
;
516 this->public.task
.process
= _process_r
;
517 this->initiator
= FALSE
;
518 this->child_create
= child_create_create(ike_sa
, NULL
, TRUE
, NULL
, NULL
);
521 return &this->public;