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 * Implementation of task_t.build for initiator, after rekeying
92 static status_t
build_i_delete(private_child_rekey_t
*this, message_t
*message
)
94 /* update exchange type to INFORMATIONAL for the delete */
95 message
->set_exchange_type(message
, INFORMATIONAL
);
97 return this->child_delete
->task
.build(&this->child_delete
->task
, message
);
101 * Implementation of task_t.process for initiator, after rekeying
103 static status_t
process_i_delete(private_child_rekey_t
*this, message_t
*message
)
105 return this->child_delete
->task
.process(&this->child_delete
->task
, message
);
109 * find a child using the REKEY_SA notify
111 static void find_child(private_child_rekey_t
*this, message_t
*message
)
113 notify_payload_t
*notify
;
114 protocol_id_t protocol
;
117 notify
= message
->get_notify(message
, REKEY_SA
);
120 protocol
= notify
->get_protocol_id(notify
);
121 spi
= notify
->get_spi(notify
);
123 if (protocol
== PROTO_ESP
|| protocol
== PROTO_AH
)
125 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, protocol
,
131 METHOD(task_t
, build_i
, status_t
,
132 private_child_rekey_t
*this, message_t
*message
)
134 notify_payload_t
*notify
;
138 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
141 { /* check if it is an outbound CHILD_SA */
142 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
145 { /* CHILD_SA is gone, unable to rekey. As an empty CREATE_CHILD_SA
146 * exchange is invalid, we fall back to an INFORMATIONAL exchange.*/
147 message
->set_exchange_type(message
, INFORMATIONAL
);
150 /* we work only with the inbound SPI */
151 this->spi
= this->child_sa
->get_spi(this->child_sa
, TRUE
);
153 config
= this->child_sa
->get_config(this->child_sa
);
155 /* we just need the rekey notify ... */
156 notify
= notify_payload_create_from_protocol_and_type(NOTIFY
,
157 this->protocol
, REKEY_SA
);
158 notify
->set_spi(notify
, this->spi
);
159 message
->add_payload(message
, (payload_t
*)notify
);
161 /* ... our CHILD_CREATE task does the hard work for us. */
162 if (!this->child_create
)
164 this->child_create
= child_create_create(this->ike_sa
, config
, TRUE
,
167 reqid
= this->child_sa
->get_reqid(this->child_sa
);
168 this->child_create
->use_reqid(this->child_create
, reqid
);
169 this->child_create
->task
.build(&this->child_create
->task
, message
);
171 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
176 METHOD(task_t
, process_r
, status_t
,
177 private_child_rekey_t
*this, message_t
*message
)
179 /* let the CHILD_CREATE task process the message */
180 this->child_create
->task
.process(&this->child_create
->task
, message
);
182 find_child(this, message
);
187 METHOD(task_t
, build_r
, status_t
,
188 private_child_rekey_t
*this, message_t
*message
)
192 if (this->child_sa
== NULL
||
193 this->child_sa
->get_state(this->child_sa
) == CHILD_DELETING
)
195 DBG1(DBG_IKE
, "unable to rekey, CHILD_SA not found");
196 message
->add_notify(message
, TRUE
, NO_PROPOSAL_CHOSEN
, chunk_empty
);
200 /* let the CHILD_CREATE task build the response */
201 reqid
= this->child_sa
->get_reqid(this->child_sa
);
202 this->child_create
->use_reqid(this->child_create
, reqid
);
203 this->child_create
->task
.build(&this->child_create
->task
, message
);
205 if (message
->get_payload(message
, SECURITY_ASSOCIATION
) == NULL
)
207 /* rekeying failed, reuse old child */
208 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
212 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
214 /* invoke rekey hook */
215 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
216 this->child_create
->get_child(this->child_create
));
221 * Handle a rekey collision
223 static child_sa_t
*handle_collision(private_child_rekey_t
*this)
225 child_sa_t
*to_delete
;
227 if (this->collision
->get_type(this->collision
) == TASK_CHILD_REKEY
)
229 chunk_t this_nonce
, other_nonce
;
230 private_child_rekey_t
*other
= (private_child_rekey_t
*)this->collision
;
232 this_nonce
= this->child_create
->get_lower_nonce(this->child_create
);
233 other_nonce
= other
->child_create
->get_lower_nonce(other
->child_create
);
235 /* if we have the lower nonce, delete rekeyed SA. If not, delete
237 if (memcmp(this_nonce
.ptr
, other_nonce
.ptr
,
238 min(this_nonce
.len
, other_nonce
.len
)) > 0)
240 child_sa_t
*child_sa
;
242 DBG1(DBG_IKE
, "CHILD_SA rekey collision won, deleting old child");
243 to_delete
= this->child_sa
;
244 /* don't touch child other created, it has already been deleted */
245 if (!this->other_child_destroyed
)
247 /* disable close action for the redundand child */
248 child_sa
= other
->child_create
->get_child(other
->child_create
);
251 child_sa
->set_close_action(child_sa
, ACTION_NONE
);
257 DBG1(DBG_IKE
, "CHILD_SA rekey collision lost, "
258 "deleting rekeyed child");
259 to_delete
= this->child_create
->get_child(this->child_create
);
264 child_delete_t
*del
= (child_delete_t
*)this->collision
;
266 /* we didn't had a chance to compare the nonces, so we delete
267 * the CHILD_SA the other is not deleting. */
268 if (del
->get_child(del
) != this->child_sa
)
270 DBG1(DBG_IKE
, "CHILD_SA rekey/delete collision, "
271 "deleting rekeyed child");
272 to_delete
= this->child_sa
;
276 DBG1(DBG_IKE
, "CHILD_SA rekey/delete collision, "
277 "deleting redundant child");
278 to_delete
= this->child_create
->get_child(this->child_create
);
284 METHOD(task_t
, process_i
, status_t
,
285 private_child_rekey_t
*this, message_t
*message
)
287 protocol_id_t protocol
;
289 child_sa_t
*to_delete
;
291 if (message
->get_notify(message
, NO_ADDITIONAL_SAS
))
293 DBG1(DBG_IKE
, "peer seems to not support CHILD_SA rekeying, "
294 "starting reauthentication");
295 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
296 lib
->processor
->queue_job(lib
->processor
,
297 (job_t
*)rekey_ike_sa_job_create(
298 this->ike_sa
->get_id(this->ike_sa
), TRUE
));
302 if (this->child_create
->task
.process(&this->child_create
->task
,
303 message
) == NEED_MORE
)
305 /* bad DH group while rekeying, try again */
306 this->child_create
->task
.migrate(&this->child_create
->task
, this->ike_sa
);
309 if (message
->get_payload(message
, SECURITY_ASSOCIATION
) == NULL
)
311 /* establishing new child failed, reuse old. but not when we
312 * received a delete in the meantime */
313 if (!(this->collision
&&
314 this->collision
->get_type(this->collision
) == TASK_CHILD_DELETE
))
317 u_int32_t retry
= RETRY_INTERVAL
- (random() % RETRY_JITTER
);
319 job
= (job_t
*)rekey_child_sa_job_create(
320 this->child_sa
->get_reqid(this->child_sa
),
321 this->child_sa
->get_protocol(this->child_sa
),
322 this->child_sa
->get_spi(this->child_sa
, TRUE
));
323 DBG1(DBG_IKE
, "CHILD_SA rekeying failed, "
324 "trying again in %d seconds", retry
);
325 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
326 lib
->scheduler
->schedule_job(lib
->scheduler
, job
, retry
);
331 /* check for rekey collisions */
334 to_delete
= handle_collision(this);
338 to_delete
= this->child_sa
;
341 if (to_delete
!= this->child_create
->get_child(this->child_create
))
342 { /* invoke rekey hook if rekeying successful */
343 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
344 this->child_create
->get_child(this->child_create
));
347 if (to_delete
== NULL
)
351 spi
= to_delete
->get_spi(to_delete
, TRUE
);
352 protocol
= to_delete
->get_protocol(to_delete
);
354 /* rekeying done, delete the obsolete CHILD_SA using a subtask */
355 this->child_delete
= child_delete_create(this->ike_sa
, protocol
, spi
);
356 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_i_delete
;
357 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_i_delete
;
362 METHOD(task_t
, get_type
, task_type_t
,
363 private_child_rekey_t
*this)
365 return TASK_CHILD_REKEY
;
368 METHOD(child_rekey_t
, collide
, void,
369 private_child_rekey_t
*this, task_t
*other
)
371 /* the task manager only detects exchange collision, but not if
372 * the collision is for the same child. we check it here. */
373 if (other
->get_type(other
) == TASK_CHILD_REKEY
)
375 private_child_rekey_t
*rekey
= (private_child_rekey_t
*)other
;
376 if (rekey
->child_sa
!= this->child_sa
)
378 /* not the same child => no collision */
379 other
->destroy(other
);
383 else if (other
->get_type(other
) == TASK_CHILD_DELETE
)
385 child_delete_t
*del
= (child_delete_t
*)other
;
386 if (del
->get_child(del
) == this->child_create
->get_child(this->child_create
))
388 /* peer deletes redundant child created in collision */
389 this->other_child_destroyed
= TRUE
;
390 other
->destroy(other
);
393 if (del
->get_child(del
) != this->child_sa
)
395 /* not the same child => no collision */
396 other
->destroy(other
);
402 /* any other task is not critical for collisisions, ignore */
403 other
->destroy(other
);
406 DBG1(DBG_IKE
, "detected %N collision with %N", task_type_names
,
407 TASK_CHILD_REKEY
, task_type_names
, other
->get_type(other
));
408 DESTROY_IF(this->collision
);
409 this->collision
= other
;
412 METHOD(task_t
, migrate
, void,
413 private_child_rekey_t
*this, ike_sa_t
*ike_sa
)
415 if (this->child_create
)
417 this->child_create
->task
.migrate(&this->child_create
->task
, ike_sa
);
419 if (this->child_delete
)
421 this->child_delete
->task
.migrate(&this->child_delete
->task
, ike_sa
);
423 DESTROY_IF(this->collision
);
425 this->ike_sa
= ike_sa
;
426 this->collision
= NULL
;
429 METHOD(task_t
, destroy
, void,
430 private_child_rekey_t
*this)
432 if (this->child_create
)
434 this->child_create
->task
.destroy(&this->child_create
->task
);
436 if (this->child_delete
)
438 this->child_delete
->task
.destroy(&this->child_delete
->task
);
440 DESTROY_IF(this->collision
);
445 * Described in header.
447 child_rekey_t
*child_rekey_create(ike_sa_t
*ike_sa
, protocol_id_t protocol
,
450 private_child_rekey_t
*this;
455 .get_type
= _get_type
,
462 .protocol
= protocol
,
466 if (protocol
!= PROTO_NONE
)
468 this->public.task
.build
= _build_i
;
469 this->public.task
.process
= _process_i
;
470 this->initiator
= TRUE
;
471 this->child_create
= NULL
;
475 this->public.task
.build
= _build_r
;
476 this->public.task
.process
= _process_r
;
477 this->initiator
= FALSE
;
478 this->child_create
= child_create_create(ike_sa
, NULL
, TRUE
, NULL
, NULL
);
481 return &this->public;