]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/charon/sa/tasks/child_rekey.c
restructured file layout
[thirdparty/strongswan.git] / src / charon / sa / tasks / child_rekey.c
1 /**
2 * @file child_rekey.c
3 *
4 * @brief Implementation of the child_rekey task.
5 *
6 */
7
8 /*
9 * Copyright (C) 2005-2007 Martin Willi
10 * Copyright (C) 2005 Jan Hutter
11 * Hochschule fuer Technik Rapperswil
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 */
23
24 #include "child_rekey.h"
25
26 #include <daemon.h>
27 #include <encoding/payloads/notify_payload.h>
28 #include <sa/tasks/child_create.h>
29 #include <sa/tasks/child_delete.h>
30 #include <processing/jobs/rekey_child_sa_job.h>
31
32
33 typedef struct private_child_rekey_t private_child_rekey_t;
34
35 /**
36 * Private members of a child_rekey_t task.
37 */
38 struct private_child_rekey_t {
39
40 /**
41 * Public methods and task_t interface.
42 */
43 child_rekey_t public;
44
45 /**
46 * Assigned IKE_SA.
47 */
48 ike_sa_t *ike_sa;
49
50 /**
51 * Are we the initiator?
52 */
53 bool initiator;
54
55 /**
56 * the CHILD_CREATE task which is reused to simplify rekeying
57 */
58 child_create_t *child_create;
59
60 /**
61 * CHILD_SA which gets rekeyed
62 */
63 child_sa_t *child_sa;
64
65 /**
66 * colliding task, may be delete or rekey
67 */
68 task_t *collision;
69 };
70
71 /**
72 * find a child using the REKEY_SA notify
73 */
74 static void find_child(private_child_rekey_t *this, message_t *message)
75 {
76 iterator_t *iterator;
77 payload_t *payload;
78
79 iterator = message->get_payload_iterator(message);
80 while (iterator->iterate(iterator, (void**)&payload))
81 {
82 notify_payload_t *notify;
83 u_int32_t spi;
84 protocol_id_t protocol;
85
86 if (payload->get_type(payload) != NOTIFY)
87 {
88 continue;
89 }
90
91 notify = (notify_payload_t*)payload;
92 protocol = notify->get_protocol_id(notify);
93 spi = notify->get_spi(notify);
94
95 if (protocol != PROTO_ESP && protocol != PROTO_AH)
96 {
97 continue;
98 }
99 this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
100 spi, FALSE);
101 break;
102
103 }
104 iterator->destroy(iterator);
105 }
106
107 /**
108 * Implementation of task_t.build for initiator
109 */
110 static status_t build_i(private_child_rekey_t *this, message_t *message)
111 {
112 notify_payload_t *notify;
113 protocol_id_t protocol;
114 u_int32_t spi, reqid;
115
116 /* we just need the rekey notify ... */
117 protocol = this->child_sa->get_protocol(this->child_sa);
118 spi = this->child_sa->get_spi(this->child_sa, TRUE);
119 notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
120 notify->set_spi(notify, spi);
121 message->add_payload(message, (payload_t*)notify);
122
123 /* ... our CHILD_CREATE task does the hard work for us. */
124 reqid = this->child_sa->get_reqid(this->child_sa);
125 this->child_create->use_reqid(this->child_create, reqid);
126 this->child_create->task.build(&this->child_create->task, message);
127
128 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
129
130 return NEED_MORE;
131 }
132
133 /**
134 * Implementation of task_t.process for initiator
135 */
136 static status_t process_r(private_child_rekey_t *this, message_t *message)
137 {
138 /* let the CHILD_CREATE task process the message */
139 this->child_create->task.process(&this->child_create->task, message);
140
141 find_child(this, message);
142
143 return NEED_MORE;
144 }
145
146 /**
147 * Implementation of task_t.build for responder
148 */
149 static status_t build_r(private_child_rekey_t *this, message_t *message)
150 {
151 u_int32_t reqid;
152
153 if (this->child_sa == NULL ||
154 this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
155 {
156 DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found");
157 message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
158 return SUCCESS;
159 }
160
161 /* let the CHILD_CREATE task build the response */
162 reqid = this->child_sa->get_reqid(this->child_sa);
163 this->child_create->use_reqid(this->child_create, reqid);
164 this->child_create->task.build(&this->child_create->task, message);
165
166 if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
167 {
168 /* rekeying failed, reuse old child */
169 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
170 return SUCCESS;
171 }
172
173 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
174 return SUCCESS;
175 }
176
177 /**
178 * Implementation of task_t.process for initiator
179 */
180 static status_t process_i(private_child_rekey_t *this, message_t *message)
181 {
182 protocol_id_t protocol;
183 u_int32_t spi;
184 child_sa_t *to_delete;
185
186 this->child_create->task.process(&this->child_create->task, message);
187 if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
188 {
189 /* establishing new child failed, reuse old. but not when we
190 * recieved a delete in the meantime */
191 if (!(this->collision &&
192 this->collision->get_type(this->collision) == CHILD_DELETE))
193 {
194 job_t *job;
195 u_int32_t retry = charon->configuration->get_retry_interval(
196 charon->configuration);
197 job = (job_t*)rekey_child_sa_job_create(
198 this->child_sa->get_reqid(this->child_sa),
199 this->child_sa->get_protocol(this->child_sa),
200 this->child_sa->get_spi(this->child_sa, TRUE));
201 DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
202 "trying again in %d seconds", retry);
203 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
204 charon->event_queue->add_relative(charon->event_queue, job, retry * 1000);
205 }
206 return SUCCESS;
207 }
208
209 to_delete = this->child_sa;
210
211 /* check for rekey collisions */
212 if (this->collision &&
213 this->collision->get_type(this->collision) == CHILD_REKEY)
214 {
215 chunk_t this_nonce, other_nonce;
216 private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
217
218 this_nonce = this->child_create->get_lower_nonce(this->child_create);
219 other_nonce = other->child_create->get_lower_nonce(other->child_create);
220
221 /* if we have the lower nonce, delete rekeyed SA. If not, delete
222 * the redundant. */
223 if (memcmp(this_nonce.ptr, other_nonce.ptr,
224 min(this_nonce.len, other_nonce.len)) < 0)
225 {
226 DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child");
227 }
228 else
229 {
230 DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child");
231 to_delete = this->child_create->get_child(this->child_create);
232 if (to_delete == NULL)
233 {
234 /* ooops, should not happen, fallback */
235 to_delete = this->child_sa;
236 }
237 }
238 }
239
240 spi = to_delete->get_spi(to_delete, TRUE);
241 protocol = to_delete->get_protocol(to_delete);
242 if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS)
243 {
244 return FAILED;
245 }
246 return SUCCESS;
247 }
248
249 /**
250 * Implementation of task_t.get_type
251 */
252 static task_type_t get_type(private_child_rekey_t *this)
253 {
254 return CHILD_REKEY;
255 }
256
257 /**
258 * Implementation of child_rekey_t.collide
259 */
260 static void collide(private_child_rekey_t *this, task_t *other)
261 {
262 /* the task manager only detects exchange collision, but not if
263 * the collision is for the same child. we check it here. */
264 if (other->get_type(other) == CHILD_REKEY)
265 {
266 private_child_rekey_t *rekey = (private_child_rekey_t*)other;
267 if (rekey == NULL || rekey->child_sa != this->child_sa)
268 {
269 /* not the same child => no collision */
270 return;
271 }
272 }
273 else if (other->get_type(other) == CHILD_DELETE)
274 {
275 child_delete_t *del = (child_delete_t*)other;
276 if (del == NULL || del->get_child(del) != this->child_sa)
277 {
278 /* not the same child => no collision */
279 return;
280 }
281 }
282 else
283 {
284 /* any other task is not critical for collisisions, ignore */
285 return;
286 }
287 DESTROY_IF(this->collision);
288 this->collision = other;
289 }
290
291 /**
292 * Implementation of task_t.migrate
293 */
294 static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
295 {
296 this->child_create->task.migrate(&this->child_create->task, ike_sa);
297 DESTROY_IF(this->collision);
298
299 this->ike_sa = ike_sa;
300 this->collision = NULL;
301 }
302
303 /**
304 * Implementation of task_t.destroy
305 */
306 static void destroy(private_child_rekey_t *this)
307 {
308 this->child_create->task.destroy(&this->child_create->task);
309 DESTROY_IF(this->collision);
310 free(this);
311 }
312
313 /*
314 * Described in header.
315 */
316 child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
317 {
318 child_cfg_t *config;
319 private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
320
321 this->public.collide = (void (*)(child_rekey_t*,task_t*))collide;
322 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
323 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
324 this->public.task.destroy = (void(*)(task_t*))destroy;
325 if (child_sa != NULL)
326 {
327 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
328 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
329 this->initiator = TRUE;
330 config = child_sa->get_config(child_sa);
331 this->child_create = child_create_create(ike_sa, config);
332 }
333 else
334 {
335 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
336 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
337 this->initiator = FALSE;
338 this->child_create = child_create_create(ike_sa, NULL);
339 }
340
341 this->ike_sa = ike_sa;
342 this->child_sa = child_sa;
343 this->collision = NULL;
344
345 return &this->public;
346 }