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/tasks/child_create.h>
22 #include <sa/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
81 * Implementation of task_t.build for initiator, after rekeying
83 static status_t
build_i_delete(private_child_rekey_t
*this, message_t
*message
)
85 /* update exchange type to INFORMATIONAL for the delete */
86 message
->set_exchange_type(message
, INFORMATIONAL
);
88 return this->child_delete
->task
.build(&this->child_delete
->task
, message
);
92 * Implementation of task_t.process for initiator, after rekeying
94 static status_t
process_i_delete(private_child_rekey_t
*this, message_t
*message
)
96 return this->child_delete
->task
.process(&this->child_delete
->task
, message
);
100 * find a child using the REKEY_SA notify
102 static void find_child(private_child_rekey_t
*this, message_t
*message
)
104 notify_payload_t
*notify
;
105 protocol_id_t protocol
;
108 notify
= message
->get_notify(message
, REKEY_SA
);
111 protocol
= notify
->get_protocol_id(notify
);
112 spi
= notify
->get_spi(notify
);
114 if (protocol
== PROTO_ESP
|| protocol
== PROTO_AH
)
116 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, protocol
,
123 * Implementation of task_t.build for initiator
125 static status_t
build_i(private_child_rekey_t
*this, message_t
*message
)
127 notify_payload_t
*notify
;
131 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
134 { /* check if it is an outbound CHILD_SA */
135 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
138 { /* CHILD_SA is gone, unable to rekey. As an empty CREATE_CHILD_SA
139 * exchange is invalid, we fall back to an INFORMATIONAL exchange.*/
140 message
->set_exchange_type(message
, INFORMATIONAL
);
143 /* we work only with the inbound SPI */
144 this->spi
= this->child_sa
->get_spi(this->child_sa
, TRUE
);
146 config
= this->child_sa
->get_config(this->child_sa
);
148 /* we just need the rekey notify ... */
149 notify
= notify_payload_create_from_protocol_and_type(this->protocol
,
151 notify
->set_spi(notify
, this->spi
);
152 message
->add_payload(message
, (payload_t
*)notify
);
154 /* ... our CHILD_CREATE task does the hard work for us. */
155 if (!this->child_create
)
157 this->child_create
= child_create_create(this->ike_sa
, config
, TRUE
,
160 reqid
= this->child_sa
->get_reqid(this->child_sa
);
161 this->child_create
->use_reqid(this->child_create
, reqid
);
162 this->child_create
->task
.build(&this->child_create
->task
, message
);
164 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
170 * Implementation of task_t.process for initiator
172 static status_t
process_r(private_child_rekey_t
*this, message_t
*message
)
174 /* let the CHILD_CREATE task process the message */
175 this->child_create
->task
.process(&this->child_create
->task
, message
);
177 find_child(this, message
);
183 * Implementation of task_t.build for responder
185 static status_t
build_r(private_child_rekey_t
*this, message_t
*message
)
189 if (this->child_sa
== NULL
||
190 this->child_sa
->get_state(this->child_sa
) == CHILD_DELETING
)
192 DBG1(DBG_IKE
, "unable to rekey, CHILD_SA not found");
193 message
->add_notify(message
, TRUE
, NO_PROPOSAL_CHOSEN
, chunk_empty
);
197 /* let the CHILD_CREATE task build the response */
198 reqid
= this->child_sa
->get_reqid(this->child_sa
);
199 this->child_create
->use_reqid(this->child_create
, reqid
);
200 this->child_create
->task
.build(&this->child_create
->task
, message
);
202 if (message
->get_payload(message
, SECURITY_ASSOCIATION
) == NULL
)
204 /* rekeying failed, reuse old child */
205 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
209 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
211 /* invoke rekey hook */
212 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
213 this->child_create
->get_child(this->child_create
));
218 * Implementation of task_t.process for initiator
220 static status_t
process_i(private_child_rekey_t
*this, message_t
*message
)
222 protocol_id_t protocol
;
224 child_sa_t
*to_delete
;
226 if (message
->get_notify(message
, NO_ADDITIONAL_SAS
))
228 DBG1(DBG_IKE
, "peer seems to not support CHILD_SA rekeying, "
229 "starting reauthentication");
230 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
231 charon
->processor
->queue_job(charon
->processor
,
232 (job_t
*)rekey_ike_sa_job_create(
233 this->ike_sa
->get_id(this->ike_sa
), TRUE
));
237 if (this->child_create
->task
.process(&this->child_create
->task
,
238 message
) == NEED_MORE
)
240 /* bad DH group while rekeying, try again */
241 this->child_create
->task
.migrate(&this->child_create
->task
, this->ike_sa
);
244 if (message
->get_payload(message
, SECURITY_ASSOCIATION
) == NULL
)
246 /* establishing new child failed, reuse old. but not when we
247 * recieved a delete in the meantime */
248 if (!(this->collision
&&
249 this->collision
->get_type(this->collision
) == CHILD_DELETE
))
252 u_int32_t retry
= RETRY_INTERVAL
- (random() % RETRY_JITTER
);
254 job
= (job_t
*)rekey_child_sa_job_create(
255 this->child_sa
->get_reqid(this->child_sa
),
256 this->child_sa
->get_protocol(this->child_sa
),
257 this->child_sa
->get_spi(this->child_sa
, TRUE
));
258 DBG1(DBG_IKE
, "CHILD_SA rekeying failed, "
259 "trying again in %d seconds", retry
);
260 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
261 charon
->scheduler
->schedule_job(charon
->scheduler
, job
, retry
);
266 to_delete
= this->child_sa
;
268 /* check for rekey collisions */
269 if (this->collision
&&
270 this->collision
->get_type(this->collision
) == CHILD_REKEY
)
272 chunk_t this_nonce
, other_nonce
;
273 private_child_rekey_t
*other
= (private_child_rekey_t
*)this->collision
;
275 this_nonce
= this->child_create
->get_lower_nonce(this->child_create
);
276 other_nonce
= other
->child_create
->get_lower_nonce(other
->child_create
);
278 /* if we have the lower nonce, delete rekeyed SA. If not, delete
280 if (memcmp(this_nonce
.ptr
, other_nonce
.ptr
,
281 min(this_nonce
.len
, other_nonce
.len
)) < 0)
283 DBG1(DBG_IKE
, "CHILD_SA rekey collision won, deleting rekeyed child");
287 DBG1(DBG_IKE
, "CHILD_SA rekey collision lost, deleting redundant child");
288 to_delete
= this->child_create
->get_child(this->child_create
);
289 if (to_delete
== NULL
)
291 /* ooops, should not happen, fallback */
292 to_delete
= this->child_sa
;
297 if (to_delete
!= this->child_create
->get_child(this->child_create
))
298 { /* invoke rekey hook if rekeying successful */
299 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
300 this->child_create
->get_child(this->child_create
));
303 spi
= to_delete
->get_spi(to_delete
, TRUE
);
304 protocol
= to_delete
->get_protocol(to_delete
);
306 /* rekeying done, delete the obsolete CHILD_SA using a subtask */
307 this->child_delete
= child_delete_create(this->ike_sa
, protocol
, spi
);
308 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_i_delete
;
309 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_i_delete
;
315 * Implementation of task_t.get_type
317 static task_type_t
get_type(private_child_rekey_t
*this)
323 * Implementation of child_rekey_t.collide
325 static void collide(private_child_rekey_t
*this, task_t
*other
)
327 /* the task manager only detects exchange collision, but not if
328 * the collision is for the same child. we check it here. */
329 if (other
->get_type(other
) == CHILD_REKEY
)
331 private_child_rekey_t
*rekey
= (private_child_rekey_t
*)other
;
332 if (rekey
== NULL
|| rekey
->child_sa
!= this->child_sa
)
334 /* not the same child => no collision */
335 other
->destroy(other
);
339 else if (other
->get_type(other
) == CHILD_DELETE
)
341 child_delete_t
*del
= (child_delete_t
*)other
;
342 if (del
== NULL
|| del
->get_child(del
) != this->child_sa
)
344 /* not the same child => no collision */
345 other
->destroy(other
);
351 /* any other task is not critical for collisisions, ignore */
352 other
->destroy(other
);
355 DESTROY_IF(this->collision
);
356 this->collision
= other
;
360 * Implementation of task_t.migrate
362 static void migrate(private_child_rekey_t
*this, ike_sa_t
*ike_sa
)
364 if (this->child_create
)
366 this->child_create
->task
.migrate(&this->child_create
->task
, ike_sa
);
368 if (this->child_delete
)
370 this->child_delete
->task
.migrate(&this->child_delete
->task
, ike_sa
);
372 DESTROY_IF(this->collision
);
374 this->ike_sa
= ike_sa
;
375 this->collision
= NULL
;
379 * Implementation of task_t.destroy
381 static void destroy(private_child_rekey_t
*this)
383 if (this->child_create
)
385 this->child_create
->task
.destroy(&this->child_create
->task
);
387 if (this->child_delete
)
389 this->child_delete
->task
.destroy(&this->child_delete
->task
);
391 DESTROY_IF(this->collision
);
396 * Described in header.
398 child_rekey_t
*child_rekey_create(ike_sa_t
*ike_sa
, protocol_id_t protocol
,
401 private_child_rekey_t
*this = malloc_thing(private_child_rekey_t
);
403 this->public.collide
= (void (*)(child_rekey_t
*,task_t
*))collide
;
404 this->public.task
.get_type
= (task_type_t(*)(task_t
*))get_type
;
405 this->public.task
.migrate
= (void(*)(task_t
*,ike_sa_t
*))migrate
;
406 this->public.task
.destroy
= (void(*)(task_t
*))destroy
;
407 if (protocol
!= PROTO_NONE
)
409 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_i
;
410 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_i
;
411 this->initiator
= TRUE
;
412 this->child_create
= NULL
;
416 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_r
;
417 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_r
;
418 this->initiator
= FALSE
;
419 this->child_create
= child_create_create(ike_sa
, NULL
, TRUE
, NULL
, NULL
);
422 this->ike_sa
= ike_sa
;
423 this->child_sa
= NULL
;
424 this->protocol
= protocol
;
426 this->collision
= NULL
;
427 this->child_delete
= NULL
;
429 return &this->public;