2 * Copyright (C) 2009-2016 Tobias Brunner
3 * Copyright (C) 2005-2007 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * HSR Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include "child_rekey.h"
21 #include <encoding/payloads/notify_payload.h>
22 #include <sa/ikev2/tasks/child_create.h>
23 #include <sa/ikev2/tasks/child_delete.h>
24 #include <processing/jobs/rekey_child_sa_job.h>
25 #include <processing/jobs/rekey_ike_sa_job.h>
28 typedef struct private_child_rekey_t private_child_rekey_t
;
31 * Private members of a child_rekey_t task.
33 struct private_child_rekey_t
{
36 * Public methods and task_t interface.
46 * Are we the initiator?
51 * Protocol of CHILD_SA to rekey
53 protocol_id_t protocol
;
56 * Inbound SPI of CHILD_SA to rekey
61 * the CHILD_CREATE task which is reused to simplify rekeying
63 child_create_t
*child_create
;
66 * the CHILD_DELETE task to delete rekeyed CHILD_SA
68 child_delete_t
*child_delete
;
71 * CHILD_SA which gets rekeyed
76 * colliding task, may be delete or rekey
81 * Indicate that peer destroyed the redundant child from collision.
82 * This happens if a peer's delete notification for the redundant
83 * child gets processed before the rekey job. If so, we must not
84 * touch the child created in the collision since it points to
85 * memory already freed.
87 bool other_child_destroyed
;
91 * Schedule a retry if rekeying temporary failed
93 static void schedule_delayed_rekey(private_child_rekey_t
*this)
98 retry
= RETRY_INTERVAL
- (random() % RETRY_JITTER
);
99 job
= (job_t
*)rekey_child_sa_job_create(
100 this->child_sa
->get_protocol(this->child_sa
),
101 this->child_sa
->get_spi(this->child_sa
, TRUE
),
102 this->ike_sa
->get_my_host(this->ike_sa
));
103 DBG1(DBG_IKE
, "CHILD_SA rekeying failed, trying again in %d seconds", retry
);
104 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
105 lib
->scheduler
->schedule_job(lib
->scheduler
, job
, retry
);
109 * Implementation of task_t.build for initiator, after rekeying
111 static status_t
build_i_delete(private_child_rekey_t
*this, message_t
*message
)
113 /* update exchange type to INFORMATIONAL for the delete */
114 message
->set_exchange_type(message
, INFORMATIONAL
);
116 return this->child_delete
->task
.build(&this->child_delete
->task
, message
);
120 * Implementation of task_t.process for initiator, after rekeying
122 static status_t
process_i_delete(private_child_rekey_t
*this, message_t
*message
)
124 return this->child_delete
->task
.process(&this->child_delete
->task
, message
);
128 * find a child using the REKEY_SA notify
130 static void find_child(private_child_rekey_t
*this, message_t
*message
)
132 notify_payload_t
*notify
;
133 protocol_id_t protocol
;
135 child_sa_t
*child_sa
;
137 notify
= message
->get_notify(message
, REKEY_SA
);
140 protocol
= notify
->get_protocol_id(notify
);
141 spi
= notify
->get_spi(notify
);
143 if (protocol
== PROTO_ESP
|| protocol
== PROTO_AH
)
145 child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, protocol
,
148 child_sa
->get_state(child_sa
) == CHILD_DELETING
&&
149 child_sa
->get_outbound_state(child_sa
) == CHILD_OUTBOUND_NONE
)
150 { /* ignore rekeyed CHILD_SAs we keep around */
153 this->child_sa
= child_sa
;
158 METHOD(task_t
, build_i
, status_t
,
159 private_child_rekey_t
*this, message_t
*message
)
161 notify_payload_t
*notify
;
165 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
168 { /* check if it is an outbound CHILD_SA */
169 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, this->protocol
,
173 /* we work only with the inbound SPI */
174 this->spi
= this->child_sa
->get_spi(this->child_sa
, TRUE
);
177 if (!this->child_sa
||
178 (!this->child_create
&&
179 this->child_sa
->get_state(this->child_sa
) != CHILD_INSTALLED
) ||
180 (this->child_create
&&
181 this->child_sa
->get_state(this->child_sa
) != CHILD_REKEYING
))
183 /* CHILD_SA is gone or in the wrong state, unable to rekey */
184 message
->set_exchange_type(message
, EXCHANGE_TYPE_UNDEFINED
);
187 config
= this->child_sa
->get_config(this->child_sa
);
190 /* our CHILD_CREATE task does the hard work for us */
191 if (!this->child_create
)
193 this->child_create
= child_create_create(this->ike_sa
,
194 config
->get_ref(config
), TRUE
, NULL
, NULL
);
196 reqid
= this->child_sa
->get_reqid(this->child_sa
);
197 this->child_create
->use_reqid(this->child_create
, reqid
);
198 this->child_create
->use_marks(this->child_create
,
199 this->child_sa
->get_mark(this->child_sa
, TRUE
).value
,
200 this->child_sa
->get_mark(this->child_sa
, FALSE
).value
);
202 if (this->child_create
->task
.build(&this->child_create
->task
,
203 message
) != NEED_MORE
)
205 schedule_delayed_rekey(this);
208 if (message
->get_exchange_type(message
) == CREATE_CHILD_SA
)
210 /* don't add the notify if the CHILD_CREATE task changed the exchange */
211 notify
= notify_payload_create_from_protocol_and_type(PLV2_NOTIFY
,
212 this->protocol
, REKEY_SA
);
213 notify
->set_spi(notify
, this->spi
);
214 message
->add_payload(message
, (payload_t
*)notify
);
216 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
221 METHOD(task_t
, process_r
, status_t
,
222 private_child_rekey_t
*this, message_t
*message
)
224 /* let the CHILD_CREATE task process the message */
225 this->child_create
->task
.process(&this->child_create
->task
, message
);
227 find_child(this, message
);
232 METHOD(task_t
, build_r
, status_t
,
233 private_child_rekey_t
*this, message_t
*message
)
237 child_sa_state_t state
;
238 child_sa_t
*child_sa
;
242 DBG1(DBG_IKE
, "unable to rekey, CHILD_SA not found");
243 message
->add_notify(message
, TRUE
, CHILD_SA_NOT_FOUND
, chunk_empty
);
246 if (this->child_sa
->get_state(this->child_sa
) == CHILD_DELETING
)
248 DBG1(DBG_IKE
, "unable to rekey, we are deleting the CHILD_SA");
249 message
->add_notify(message
, TRUE
, TEMPORARY_FAILURE
, chunk_empty
);
253 /* let the CHILD_CREATE task build the response */
254 reqid
= this->child_sa
->get_reqid(this->child_sa
);
255 this->child_create
->use_reqid(this->child_create
, reqid
);
256 this->child_create
->use_marks(this->child_create
,
257 this->child_sa
->get_mark(this->child_sa
, TRUE
).value
,
258 this->child_sa
->get_mark(this->child_sa
, FALSE
).value
);
259 config
= this->child_sa
->get_config(this->child_sa
);
260 this->child_create
->set_config(this->child_create
, config
->get_ref(config
));
261 this->child_create
->task
.build(&this->child_create
->task
, message
);
263 state
= this->child_sa
->get_state(this->child_sa
);
264 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
266 if (message
->get_payload(message
, PLV2_SECURITY_ASSOCIATION
) == NULL
)
267 { /* rekeying failed, reuse old child */
268 this->child_sa
->set_state(this->child_sa
, state
);
272 child_sa
= this->child_create
->get_child(this->child_create
);
273 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYED
);
274 this->child_sa
->set_rekey_spi(this->child_sa
,
275 child_sa
->get_spi(child_sa
, FALSE
));
277 /* invoke rekey hook */
278 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
279 this->child_create
->get_child(this->child_create
));
284 * Handle a rekey collision
286 static child_sa_t
*handle_collision(private_child_rekey_t
*this)
288 child_sa_t
*to_delete
;
290 if (this->collision
->get_type(this->collision
) == TASK_CHILD_REKEY
)
292 chunk_t this_nonce
, other_nonce
;
293 private_child_rekey_t
*other
= (private_child_rekey_t
*)this->collision
;
295 this_nonce
= this->child_create
->get_lower_nonce(this->child_create
);
296 other_nonce
= other
->child_create
->get_lower_nonce(other
->child_create
);
298 /* if we have the lower nonce, delete rekeyed SA. If not, delete
300 if (memcmp(this_nonce
.ptr
, other_nonce
.ptr
,
301 min(this_nonce
.len
, other_nonce
.len
)) > 0)
303 child_sa_t
*child_sa
;
305 DBG1(DBG_IKE
, "CHILD_SA rekey collision won, deleting old child");
306 to_delete
= this->child_sa
;
307 /* don't touch child other created, it has already been deleted */
308 if (!this->other_child_destroyed
)
310 /* disable close action and updown event for redundant child */
311 child_sa
= other
->child_create
->get_child(other
->child_create
);
314 child_sa
->set_close_action(child_sa
, ACTION_NONE
);
315 if (child_sa
->get_state(child_sa
) != CHILD_REKEYED
)
317 child_sa
->set_state(child_sa
, CHILD_REKEYED
);
324 DBG1(DBG_IKE
, "CHILD_SA rekey collision lost, "
325 "deleting rekeyed child");
326 to_delete
= this->child_create
->get_child(this->child_create
);
331 child_delete_t
*del
= (child_delete_t
*)this->collision
;
333 /* we didn't had a chance to compare the nonces, so we delete
334 * the CHILD_SA the other is not deleting. */
335 if (del
->get_child(del
) != this->child_sa
)
337 DBG1(DBG_IKE
, "CHILD_SA rekey/delete collision, "
338 "deleting rekeyed child");
339 to_delete
= this->child_sa
;
343 DBG1(DBG_IKE
, "CHILD_SA rekey/delete collision, "
344 "deleting redundant child");
345 to_delete
= this->child_create
->get_child(this->child_create
);
351 METHOD(task_t
, process_i
, status_t
,
352 private_child_rekey_t
*this, message_t
*message
)
354 protocol_id_t protocol
;
356 child_sa_t
*to_delete
;
358 if (message
->get_notify(message
, NO_ADDITIONAL_SAS
))
360 DBG1(DBG_IKE
, "peer seems to not support CHILD_SA rekeying, "
361 "starting reauthentication");
362 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
363 lib
->processor
->queue_job(lib
->processor
,
364 (job_t
*)rekey_ike_sa_job_create(
365 this->ike_sa
->get_id(this->ike_sa
), TRUE
));
368 if (message
->get_notify(message
, CHILD_SA_NOT_FOUND
))
370 child_cfg_t
*child_cfg
;
373 if (this->collision
&&
374 this->collision
->get_type(this->collision
) == TASK_CHILD_DELETE
)
375 { /* ignore this error if we already deleted the CHILD_SA on the
376 * peer's behalf (could happen if the other peer does not detect
377 * the collision and did not respond with TEMPORARY_FAILURE) */
380 DBG1(DBG_IKE
, "peer didn't find the CHILD_SA we tried to rekey");
381 /* FIXME: according to RFC 7296 we should only create a new CHILD_SA if
382 * it does not exist yet, we currently have no good way of checking for
383 * that (we could go by name, but that might be tricky e.g. due to
385 spi
= this->child_sa
->get_spi(this->child_sa
, TRUE
);
386 reqid
= this->child_sa
->get_reqid(this->child_sa
);
387 protocol
= this->child_sa
->get_protocol(this->child_sa
);
388 child_cfg
= this->child_sa
->get_config(this->child_sa
);
389 child_cfg
->get_ref(child_cfg
);
390 charon
->bus
->child_updown(charon
->bus
, this->child_sa
, FALSE
);
391 this->ike_sa
->destroy_child_sa(this->ike_sa
, protocol
, spi
);
392 return this->ike_sa
->initiate(this->ike_sa
,
393 child_cfg
->get_ref(child_cfg
), reqid
,
397 if (this->child_create
->task
.process(&this->child_create
->task
,
398 message
) == NEED_MORE
)
400 /* bad DH group while rekeying, retry, or failure requiring deletion */
403 if (message
->get_payload(message
, PLV2_SECURITY_ASSOCIATION
) == NULL
)
405 /* establishing new child failed, reuse old and try again. but not when
406 * we received a delete in the meantime */
407 if (!this->collision
||
408 this->collision
->get_type(this->collision
) != TASK_CHILD_DELETE
)
410 schedule_delayed_rekey(this);
415 /* check for rekey collisions */
418 to_delete
= handle_collision(this);
422 to_delete
= this->child_sa
;
425 if (to_delete
!= this->child_create
->get_child(this->child_create
))
426 { /* invoke rekey hook if rekeying successful */
427 charon
->bus
->child_rekey(charon
->bus
, this->child_sa
,
428 this->child_create
->get_child(this->child_create
));
431 if (to_delete
== NULL
)
435 /* disable updown event for redundant CHILD_SA */
436 if (to_delete
->get_state(to_delete
) != CHILD_REKEYED
)
438 to_delete
->set_state(to_delete
, CHILD_REKEYED
);
440 spi
= to_delete
->get_spi(to_delete
, TRUE
);
441 protocol
= to_delete
->get_protocol(to_delete
);
443 /* rekeying done, delete the obsolete CHILD_SA using a subtask */
444 this->child_delete
= child_delete_create(this->ike_sa
, protocol
, spi
, FALSE
);
445 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_i_delete
;
446 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_i_delete
;
451 METHOD(task_t
, get_type
, task_type_t
,
452 private_child_rekey_t
*this)
454 return TASK_CHILD_REKEY
;
457 METHOD(child_rekey_t
, is_redundant
, bool,
458 private_child_rekey_t
*this, child_sa_t
*child
)
460 if (this->collision
&&
461 this->collision
->get_type(this->collision
) == TASK_CHILD_REKEY
)
463 private_child_rekey_t
*rekey
= (private_child_rekey_t
*)this->collision
;
464 return child
== rekey
->child_create
->get_child(rekey
->child_create
);
469 METHOD(child_rekey_t
, collide
, void,
470 private_child_rekey_t
*this, task_t
*other
)
472 /* the task manager only detects exchange collision, but not if
473 * the collision is for the same child. we check it here. */
474 if (other
->get_type(other
) == TASK_CHILD_REKEY
)
476 private_child_rekey_t
*rekey
= (private_child_rekey_t
*)other
;
477 child_sa_t
*other_child
;
479 if (rekey
->child_sa
!= this->child_sa
)
480 { /* not the same child => no collision */
481 other
->destroy(other
);
484 /* ignore passive tasks that did not successfully create a CHILD_SA */
485 other_child
= rekey
->child_create
->get_child(rekey
->child_create
);
487 other_child
->get_state(other_child
) != CHILD_INSTALLED
)
489 other
->destroy(other
);
493 else if (other
->get_type(other
) == TASK_CHILD_DELETE
)
495 child_delete_t
*del
= (child_delete_t
*)other
;
496 if (is_redundant(this, del
->get_child(del
)))
498 this->other_child_destroyed
= TRUE
;
499 other
->destroy(other
);
502 if (del
->get_child(del
) != this->child_sa
)
504 /* not the same child => no collision */
505 other
->destroy(other
);
511 /* any other task is not critical for collisions, ignore */
512 other
->destroy(other
);
515 DBG1(DBG_IKE
, "detected %N collision with %N", task_type_names
,
516 TASK_CHILD_REKEY
, task_type_names
, other
->get_type(other
));
517 DESTROY_IF(this->collision
);
518 this->collision
= other
;
521 METHOD(task_t
, migrate
, void,
522 private_child_rekey_t
*this, ike_sa_t
*ike_sa
)
524 if (this->child_create
)
526 this->child_create
->task
.migrate(&this->child_create
->task
, ike_sa
);
528 if (this->child_delete
)
530 this->child_delete
->task
.migrate(&this->child_delete
->task
, ike_sa
);
532 DESTROY_IF(this->collision
);
534 this->ike_sa
= ike_sa
;
535 this->collision
= NULL
;
538 METHOD(task_t
, destroy
, void,
539 private_child_rekey_t
*this)
541 if (this->child_create
)
543 this->child_create
->task
.destroy(&this->child_create
->task
);
545 if (this->child_delete
)
547 this->child_delete
->task
.destroy(&this->child_delete
->task
);
549 DESTROY_IF(this->collision
);
554 * Described in header.
556 child_rekey_t
*child_rekey_create(ike_sa_t
*ike_sa
, protocol_id_t protocol
,
559 private_child_rekey_t
*this;
564 .get_type
= _get_type
,
568 .is_redundant
= _is_redundant
,
572 .protocol
= protocol
,
576 if (protocol
!= PROTO_NONE
)
578 this->public.task
.build
= _build_i
;
579 this->public.task
.process
= _process_i
;
580 this->initiator
= TRUE
;
581 this->child_create
= NULL
;
585 this->public.task
.build
= _build_r
;
586 this->public.task
.process
= _process_r
;
587 this->initiator
= FALSE
;
588 this->child_create
= child_create_create(ike_sa
, NULL
, TRUE
, NULL
, NULL
);
591 return &this->public;