3a22d50404c333f4666b1fe454333545b12b3229
[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 * the CHILD_CREATE task which is reused to simplify rekeying
53 */
54 child_create_t *child_create;
55
56 /**
57 * CHILD_SA which gets rekeyed
58 */
59 child_sa_t *child_sa;
60
61 /**
62 * colliding task, may be delete or rekey
63 */
64 task_t *collision;
65 };
66
67 /**
68 * find a child using the REKEY_SA notify
69 */
70 static void find_child(private_child_rekey_t *this, message_t *message)
71 {
72 iterator_t *iterator;
73 payload_t *payload;
74
75 iterator = message->get_payload_iterator(message);
76 while (iterator->iterate(iterator, (void**)&payload))
77 {
78 notify_payload_t *notify;
79 u_int32_t spi;
80 protocol_id_t protocol;
81
82 if (payload->get_type(payload) != NOTIFY)
83 {
84 continue;
85 }
86
87 notify = (notify_payload_t*)payload;
88 protocol = notify->get_protocol_id(notify);
89 spi = notify->get_spi(notify);
90
91 if (protocol != PROTO_ESP && protocol != PROTO_AH)
92 {
93 continue;
94 }
95 this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
96 spi, FALSE);
97 break;
98
99 }
100 iterator->destroy(iterator);
101 }
102
103 /**
104 * Implementation of task_t.build for initiator
105 */
106 static status_t build_i(private_child_rekey_t *this, message_t *message)
107 {
108 notify_payload_t *notify;
109 protocol_id_t protocol;
110 u_int32_t spi, reqid;
111
112 /* we just need the rekey notify ... */
113 protocol = this->child_sa->get_protocol(this->child_sa);
114 spi = this->child_sa->get_spi(this->child_sa, TRUE);
115 notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
116 notify->set_spi(notify, spi);
117 message->add_payload(message, (payload_t*)notify);
118
119 /* ... our CHILD_CREATE task does the hard work for us. */
120 reqid = this->child_sa->get_reqid(this->child_sa);
121 this->child_create->use_reqid(this->child_create, reqid);
122 this->child_create->task.build(&this->child_create->task, message);
123
124 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
125
126 return NEED_MORE;
127 }
128
129 /**
130 * Implementation of task_t.process for initiator
131 */
132 static status_t process_r(private_child_rekey_t *this, message_t *message)
133 {
134 /* let the CHILD_CREATE task process the message */
135 this->child_create->task.process(&this->child_create->task, message);
136
137 find_child(this, message);
138
139 return NEED_MORE;
140 }
141
142 /**
143 * Implementation of task_t.build for responder
144 */
145 static status_t build_r(private_child_rekey_t *this, message_t *message)
146 {
147 u_int32_t reqid;
148
149 if (this->child_sa == NULL ||
150 this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
151 {
152 DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found");
153 message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
154 return SUCCESS;
155 }
156
157 /* let the CHILD_CREATE task build the response */
158 reqid = this->child_sa->get_reqid(this->child_sa);
159 this->child_create->use_reqid(this->child_create, reqid);
160 this->child_create->task.build(&this->child_create->task, message);
161
162 if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
163 {
164 /* rekeying failed, reuse old child */
165 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
166 return SUCCESS;
167 }
168
169 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
170 return SUCCESS;
171 }
172
173 /**
174 * Implementation of task_t.process for initiator
175 */
176 static status_t process_i(private_child_rekey_t *this, message_t *message)
177 {
178 protocol_id_t protocol;
179 u_int32_t spi;
180 child_sa_t *to_delete;
181 iterator_t *iterator;
182 payload_t *payload;
183
184 /* handle NO_ADDITIONAL_SAS notify */
185 iterator = message->get_payload_iterator(message);
186 while (iterator->iterate(iterator, (void**)&payload))
187 {
188 if (payload->get_type(payload) == NOTIFY)
189 {
190 notify_payload_t *notify = (notify_payload_t*)payload;
191
192 if (notify->get_notify_type(notify) == NO_ADDITIONAL_SAS)
193 {
194 DBG1(DBG_IKE, "peer seems to not support CHILD_SA rekeying, "
195 "starting reauthentication");
196 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
197 charon->processor->queue_job(charon->processor,
198 (job_t*)rekey_ike_sa_job_create(
199 this->ike_sa->get_id(this->ike_sa), TRUE));
200 iterator->destroy(iterator);
201 return SUCCESS;
202 }
203 }
204 }
205 iterator->destroy(iterator);
206
207 if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE)
208 {
209 /* bad DH group while rekeying, try again */
210 this->child_create->task.migrate(&this->child_create->task, this->ike_sa);
211 return NEED_MORE;
212 }
213 if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
214 {
215 /* establishing new child failed, reuse old. but not when we
216 * recieved a delete in the meantime */
217 if (!(this->collision &&
218 this->collision->get_type(this->collision) == CHILD_DELETE))
219 {
220 job_t *job;
221 u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
222
223 job = (job_t*)rekey_child_sa_job_create(
224 this->child_sa->get_reqid(this->child_sa),
225 this->child_sa->get_protocol(this->child_sa),
226 this->child_sa->get_spi(this->child_sa, TRUE));
227 DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
228 "trying again in %d seconds", retry);
229 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
230 charon->scheduler->schedule_job(charon->scheduler, job, retry * 1000);
231 }
232 return SUCCESS;
233 }
234
235 to_delete = this->child_sa;
236
237 /* check for rekey collisions */
238 if (this->collision &&
239 this->collision->get_type(this->collision) == CHILD_REKEY)
240 {
241 chunk_t this_nonce, other_nonce;
242 private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
243
244 this_nonce = this->child_create->get_lower_nonce(this->child_create);
245 other_nonce = other->child_create->get_lower_nonce(other->child_create);
246
247 /* if we have the lower nonce, delete rekeyed SA. If not, delete
248 * the redundant. */
249 if (memcmp(this_nonce.ptr, other_nonce.ptr,
250 min(this_nonce.len, other_nonce.len)) < 0)
251 {
252 DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child");
253 }
254 else
255 {
256 DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child");
257 to_delete = this->child_create->get_child(this->child_create);
258 if (to_delete == NULL)
259 {
260 /* ooops, should not happen, fallback */
261 to_delete = this->child_sa;
262 }
263 }
264 }
265
266 spi = to_delete->get_spi(to_delete, TRUE);
267 protocol = to_delete->get_protocol(to_delete);
268 if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS)
269 {
270 return FAILED;
271 }
272 return SUCCESS;
273 }
274
275 /**
276 * Implementation of task_t.get_type
277 */
278 static task_type_t get_type(private_child_rekey_t *this)
279 {
280 return CHILD_REKEY;
281 }
282
283 /**
284 * Implementation of child_rekey_t.collide
285 */
286 static void collide(private_child_rekey_t *this, task_t *other)
287 {
288 /* the task manager only detects exchange collision, but not if
289 * the collision is for the same child. we check it here. */
290 if (other->get_type(other) == CHILD_REKEY)
291 {
292 private_child_rekey_t *rekey = (private_child_rekey_t*)other;
293 if (rekey == NULL || rekey->child_sa != this->child_sa)
294 {
295 /* not the same child => no collision */
296 return;
297 }
298 }
299 else if (other->get_type(other) == CHILD_DELETE)
300 {
301 child_delete_t *del = (child_delete_t*)other;
302 if (del == NULL || del->get_child(del) != this->child_sa)
303 {
304 /* not the same child => no collision */
305 return;
306 }
307 }
308 else
309 {
310 /* any other task is not critical for collisisions, ignore */
311 return;
312 }
313 DESTROY_IF(this->collision);
314 this->collision = other;
315 }
316
317 /**
318 * Implementation of task_t.migrate
319 */
320 static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
321 {
322 this->child_create->task.migrate(&this->child_create->task, ike_sa);
323 DESTROY_IF(this->collision);
324
325 this->ike_sa = ike_sa;
326 this->collision = NULL;
327 }
328
329 /**
330 * Implementation of task_t.destroy
331 */
332 static void destroy(private_child_rekey_t *this)
333 {
334 this->child_create->task.destroy(&this->child_create->task);
335 DESTROY_IF(this->collision);
336 free(this);
337 }
338
339 /*
340 * Described in header.
341 */
342 child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
343 {
344 child_cfg_t *config;
345 private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
346
347 this->public.collide = (void (*)(child_rekey_t*,task_t*))collide;
348 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
349 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
350 this->public.task.destroy = (void(*)(task_t*))destroy;
351 if (child_sa != NULL)
352 {
353 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
354 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
355 this->initiator = TRUE;
356 config = child_sa->get_config(child_sa);
357 this->child_create = child_create_create(ike_sa, config);
358 }
359 else
360 {
361 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
362 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
363 this->initiator = FALSE;
364 this->child_create = child_create_create(ike_sa, NULL);
365 }
366
367 this->ike_sa = ike_sa;
368 this->child_sa = child_sa;
369 this->collision = NULL;
370
371 return &this->public;
372 }