schedule_job uses seconds to support time values larger than 49 days
[strongswan.git] / src / charon / sa / tasks / child_rekey.c
1 /*
2 * Copyright (C) 2005-2007 Martin Willi
3 * Copyright (C) 2005 Jan Hutter
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 * $Id$
17 */
18
19 #include "child_rekey.h"
20
21 #include <daemon.h>
22 #include <encoding/payloads/notify_payload.h>
23 #include <sa/tasks/child_create.h>
24 #include <sa/tasks/child_delete.h>
25 #include <processing/jobs/rekey_child_sa_job.h>
26 #include <processing/jobs/rekey_ike_sa_job.h>
27
28
29 typedef struct private_child_rekey_t private_child_rekey_t;
30
31 /**
32 * Private members of a child_rekey_t task.
33 */
34 struct private_child_rekey_t {
35
36 /**
37 * Public methods and task_t interface.
38 */
39 child_rekey_t public;
40
41 /**
42 * Assigned IKE_SA.
43 */
44 ike_sa_t *ike_sa;
45
46 /**
47 * Are we the initiator?
48 */
49 bool initiator;
50
51 /**
52 * Protocol of CHILD_SA to rekey
53 */
54 protocol_id_t protocol;
55
56 /**
57 * Inbound SPI of CHILD_SA to rekey
58 */
59 u_int32_t spi;
60
61 /**
62 * the CHILD_CREATE task which is reused to simplify rekeying
63 */
64 child_create_t *child_create;
65
66 /**
67 * the CHILD_DELETE task to delete rekeyed CHILD_SA
68 */
69 child_delete_t *child_delete;
70
71 /**
72 * CHILD_SA which gets rekeyed
73 */
74 child_sa_t *child_sa;
75
76 /**
77 * colliding task, may be delete or rekey
78 */
79 task_t *collision;
80 };
81
82 /**
83 * Implementation of task_t.build for initiator, after rekeying
84 */
85 static status_t build_i_delete(private_child_rekey_t *this, message_t *message)
86 {
87 /* update exchange type to INFORMATIONAL for the delete */
88 message->set_exchange_type(message, INFORMATIONAL);
89
90 return this->child_delete->task.build(&this->child_delete->task, message);
91 }
92
93 /**
94 * Implementation of task_t.process for initiator, after rekeying
95 */
96 static status_t process_i_delete(private_child_rekey_t *this, message_t *message)
97 {
98 return this->child_delete->task.process(&this->child_delete->task, message);
99 }
100
101 /**
102 * find a child using the REKEY_SA notify
103 */
104 static void find_child(private_child_rekey_t *this, message_t *message)
105 {
106 enumerator_t *enumerator;
107 payload_t *payload;
108
109 enumerator = message->create_payload_enumerator(message);
110 while (enumerator->enumerate(enumerator, &payload))
111 {
112 notify_payload_t *notify;
113 u_int32_t spi;
114 protocol_id_t protocol;
115
116 if (payload->get_type(payload) != NOTIFY)
117 {
118 continue;
119 }
120
121 notify = (notify_payload_t*)payload;
122 protocol = notify->get_protocol_id(notify);
123 spi = notify->get_spi(notify);
124
125 if (protocol != PROTO_ESP && protocol != PROTO_AH)
126 {
127 continue;
128 }
129 this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
130 spi, FALSE);
131 break;
132
133 }
134 enumerator->destroy(enumerator);
135 }
136
137 /**
138 * Implementation of task_t.build for initiator
139 */
140 static status_t build_i(private_child_rekey_t *this, message_t *message)
141 {
142 notify_payload_t *notify;
143 u_int32_t reqid;
144 child_cfg_t *config;
145
146 this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
147 this->spi, TRUE);
148 if (!this->child_sa)
149 { /* CHILD_SA is gone, unable to rekey */
150 return SUCCESS;
151 }
152 config = this->child_sa->get_config(this->child_sa);
153
154 /* we just need the rekey notify ... */
155 notify = notify_payload_create_from_protocol_and_type(this->protocol,
156 REKEY_SA);
157 notify->set_spi(notify, this->spi);
158 message->add_payload(message, (payload_t*)notify);
159
160 /* ... our CHILD_CREATE task does the hard work for us. */
161 reqid = this->child_sa->get_reqid(this->child_sa);
162 this->child_create = child_create_create(this->ike_sa, config);
163 this->child_create->use_reqid(this->child_create, reqid);
164 this->child_create->task.build(&this->child_create->task, message);
165
166 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
167
168 return NEED_MORE;
169 }
170
171 /**
172 * Implementation of task_t.process for initiator
173 */
174 static status_t process_r(private_child_rekey_t *this, message_t *message)
175 {
176 /* let the CHILD_CREATE task process the message */
177 this->child_create->task.process(&this->child_create->task, message);
178
179 find_child(this, message);
180
181 return NEED_MORE;
182 }
183
184 /**
185 * Implementation of task_t.build for responder
186 */
187 static status_t build_r(private_child_rekey_t *this, message_t *message)
188 {
189 u_int32_t reqid;
190
191 if (this->child_sa == NULL ||
192 this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
193 {
194 DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found");
195 message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
196 return SUCCESS;
197 }
198
199 /* let the CHILD_CREATE task build the response */
200 reqid = this->child_sa->get_reqid(this->child_sa);
201 this->child_create->use_reqid(this->child_create, reqid);
202 this->child_create->task.build(&this->child_create->task, message);
203
204 if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
205 {
206 /* rekeying failed, reuse old child */
207 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
208 return SUCCESS;
209 }
210
211 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
212 return SUCCESS;
213 }
214
215 /**
216 * Implementation of task_t.process for initiator
217 */
218 static status_t process_i(private_child_rekey_t *this, message_t *message)
219 {
220 protocol_id_t protocol;
221 u_int32_t spi;
222 child_sa_t *to_delete;
223 enumerator_t *enumerator;
224 payload_t *payload;
225
226 /* handle NO_ADDITIONAL_SAS notify */
227 enumerator = message->create_payload_enumerator(message);
228 while (enumerator->enumerate(enumerator, &payload))
229 {
230 if (payload->get_type(payload) == NOTIFY)
231 {
232 notify_payload_t *notify = (notify_payload_t*)payload;
233
234 if (notify->get_notify_type(notify) == NO_ADDITIONAL_SAS)
235 {
236 DBG1(DBG_IKE, "peer seems to not support CHILD_SA rekeying, "
237 "starting reauthentication");
238 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
239 charon->processor->queue_job(charon->processor,
240 (job_t*)rekey_ike_sa_job_create(
241 this->ike_sa->get_id(this->ike_sa), TRUE));
242 enumerator->destroy(enumerator);
243 return SUCCESS;
244 }
245 }
246 }
247 enumerator->destroy(enumerator);
248
249 if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE)
250 {
251 /* bad DH group while rekeying, try again */
252 this->child_create->task.migrate(&this->child_create->task, this->ike_sa);
253 return NEED_MORE;
254 }
255 if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
256 {
257 /* establishing new child failed, reuse old. but not when we
258 * recieved a delete in the meantime */
259 if (!(this->collision &&
260 this->collision->get_type(this->collision) == CHILD_DELETE))
261 {
262 job_t *job;
263 u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
264
265 job = (job_t*)rekey_child_sa_job_create(
266 this->child_sa->get_reqid(this->child_sa),
267 this->child_sa->get_protocol(this->child_sa),
268 this->child_sa->get_spi(this->child_sa, TRUE));
269 DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
270 "trying again in %d seconds", retry);
271 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
272 charon->scheduler->schedule_job(charon->scheduler, job, retry);
273 }
274 return SUCCESS;
275 }
276
277 to_delete = this->child_sa;
278
279 /* check for rekey collisions */
280 if (this->collision &&
281 this->collision->get_type(this->collision) == CHILD_REKEY)
282 {
283 chunk_t this_nonce, other_nonce;
284 private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
285
286 this_nonce = this->child_create->get_lower_nonce(this->child_create);
287 other_nonce = other->child_create->get_lower_nonce(other->child_create);
288
289 /* if we have the lower nonce, delete rekeyed SA. If not, delete
290 * the redundant. */
291 if (memcmp(this_nonce.ptr, other_nonce.ptr,
292 min(this_nonce.len, other_nonce.len)) < 0)
293 {
294 DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child");
295 }
296 else
297 {
298 DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child");
299 to_delete = this->child_create->get_child(this->child_create);
300 if (to_delete == NULL)
301 {
302 /* ooops, should not happen, fallback */
303 to_delete = this->child_sa;
304 }
305 }
306 }
307
308 spi = to_delete->get_spi(to_delete, TRUE);
309 protocol = to_delete->get_protocol(to_delete);
310
311 /* rekeying done, delete the obsolete CHILD_SA using a subtask */
312 this->child_delete = child_delete_create(this->ike_sa, protocol, spi);
313 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete;
314 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete;
315
316 return NEED_MORE;
317 }
318
319 /**
320 * Implementation of task_t.get_type
321 */
322 static task_type_t get_type(private_child_rekey_t *this)
323 {
324 return CHILD_REKEY;
325 }
326
327 /**
328 * Implementation of child_rekey_t.collide
329 */
330 static void collide(private_child_rekey_t *this, task_t *other)
331 {
332 /* the task manager only detects exchange collision, but not if
333 * the collision is for the same child. we check it here. */
334 if (other->get_type(other) == CHILD_REKEY)
335 {
336 private_child_rekey_t *rekey = (private_child_rekey_t*)other;
337 if (rekey == NULL || rekey->child_sa != this->child_sa)
338 {
339 /* not the same child => no collision */
340 return;
341 }
342 }
343 else if (other->get_type(other) == CHILD_DELETE)
344 {
345 child_delete_t *del = (child_delete_t*)other;
346 if (del == NULL || del->get_child(del) != this->child_sa)
347 {
348 /* not the same child => no collision */
349 return;
350 }
351 }
352 else
353 {
354 /* any other task is not critical for collisisions, ignore */
355 return;
356 }
357 DESTROY_IF(this->collision);
358 this->collision = other;
359 }
360
361 /**
362 * Implementation of task_t.migrate
363 */
364 static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
365 {
366 if (this->child_create)
367 {
368 this->child_create->task.migrate(&this->child_create->task, ike_sa);
369 }
370 if (this->child_delete)
371 {
372 this->child_delete->task.migrate(&this->child_delete->task, ike_sa);
373 }
374 DESTROY_IF(this->collision);
375
376 this->ike_sa = ike_sa;
377 this->collision = NULL;
378 }
379
380 /**
381 * Implementation of task_t.destroy
382 */
383 static void destroy(private_child_rekey_t *this)
384 {
385 if (this->child_create)
386 {
387 this->child_create->task.destroy(&this->child_create->task);
388 }
389 if (this->child_delete)
390 {
391 this->child_delete->task.destroy(&this->child_delete->task);
392 }
393 DESTROY_IF(this->collision);
394 free(this);
395 }
396
397 /*
398 * Described in header.
399 */
400 child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
401 u_int32_t spi)
402 {
403 private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
404
405 this->public.collide = (void (*)(child_rekey_t*,task_t*))collide;
406 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
407 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
408 this->public.task.destroy = (void(*)(task_t*))destroy;
409 if (protocol != PROTO_NONE)
410 {
411 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
412 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
413 this->initiator = TRUE;
414 this->child_create = NULL;
415 }
416 else
417 {
418 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
419 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
420 this->initiator = FALSE;
421 this->child_create = child_create_create(ike_sa, NULL);
422 }
423
424 this->ike_sa = ike_sa;
425 this->child_sa = NULL;
426 this->protocol = protocol;
427 this->spi = spi;
428 this->collision = NULL;
429 this->child_delete = NULL;
430
431 return &this->public;
432 }