3fb4a54f6ce87f28fe3334f576f5255a8d4a6ca8
[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
30
31 typedef struct private_child_rekey_t private_child_rekey_t;
32
33 /**
34 * Private members of a child_rekey_t task.
35 */
36 struct private_child_rekey_t {
37
38 /**
39 * Public methods and task_t interface.
40 */
41 child_rekey_t public;
42
43 /**
44 * Assigned IKE_SA.
45 */
46 ike_sa_t *ike_sa;
47
48 /**
49 * Are we the initiator?
50 */
51 bool initiator;
52
53 /**
54 * the CHILD_CREATE task which is reused to simplify rekeying
55 */
56 child_create_t *child_create;
57
58 /**
59 * CHILD_SA which gets rekeyed
60 */
61 child_sa_t *child_sa;
62
63 /**
64 * colliding task, may be delete or rekey
65 */
66 task_t *collision;
67 };
68
69 /**
70 * find a child using the REKEY_SA notify
71 */
72 static void find_child(private_child_rekey_t *this, message_t *message)
73 {
74 iterator_t *iterator;
75 payload_t *payload;
76
77 iterator = message->get_payload_iterator(message);
78 while (iterator->iterate(iterator, (void**)&payload))
79 {
80 notify_payload_t *notify;
81 u_int32_t spi;
82 protocol_id_t protocol;
83
84 if (payload->get_type(payload) != NOTIFY)
85 {
86 continue;
87 }
88
89 notify = (notify_payload_t*)payload;
90 protocol = notify->get_protocol_id(notify);
91 spi = notify->get_spi(notify);
92
93 if (protocol != PROTO_ESP && protocol != PROTO_AH)
94 {
95 continue;
96 }
97 this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
98 spi, FALSE);
99 break;
100
101 }
102 iterator->destroy(iterator);
103 }
104
105 /**
106 * Implementation of task_t.build for initiator
107 */
108 static status_t build_i(private_child_rekey_t *this, message_t *message)
109 {
110 notify_payload_t *notify;
111 protocol_id_t protocol;
112 u_int32_t spi, reqid;
113
114 /* we just need the rekey notify ... */
115 protocol = this->child_sa->get_protocol(this->child_sa);
116 spi = this->child_sa->get_spi(this->child_sa, TRUE);
117 notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
118 notify->set_spi(notify, spi);
119 message->add_payload(message, (payload_t*)notify);
120
121 /* ... our CHILD_CREATE task does the hard work for us. */
122 reqid = this->child_sa->get_reqid(this->child_sa);
123 this->child_create->use_reqid(this->child_create, reqid);
124 this->child_create->task.build(&this->child_create->task, message);
125
126 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
127
128 return NEED_MORE;
129 }
130
131 /**
132 * Implementation of task_t.process for initiator
133 */
134 static status_t process_r(private_child_rekey_t *this, message_t *message)
135 {
136 /* let the CHILD_CREATE task process the message */
137 this->child_create->task.process(&this->child_create->task, message);
138
139 find_child(this, message);
140
141 return NEED_MORE;
142 }
143
144 /**
145 * Implementation of task_t.build for responder
146 */
147 static status_t build_r(private_child_rekey_t *this, message_t *message)
148 {
149 u_int32_t reqid;
150
151 if (this->child_sa == NULL ||
152 this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
153 {
154 DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found");
155 message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
156 return SUCCESS;
157 }
158
159 /* let the CHILD_CREATE task build the response */
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);
163
164 if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
165 {
166 /* rekeying failed, reuse old child */
167 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
168 /* TODO: reschedule rekeying */
169 return SUCCESS;
170 }
171
172 this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
173
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 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
195 /* TODO: rescedule rekeying */
196 }
197 return SUCCESS;
198 }
199
200 to_delete = this->child_sa;
201
202 /* check for rekey collisions */
203 if (this->collision &&
204 this->collision->get_type(this->collision) == CHILD_REKEY)
205 {
206 chunk_t this_nonce, other_nonce;
207 private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
208
209 this_nonce = this->child_create->get_lower_nonce(this->child_create);
210 other_nonce = other->child_create->get_lower_nonce(other->child_create);
211
212 /* if we have the lower nonce, delete rekeyed SA. If not, delete
213 * the redundant. */
214 if (memcmp(this_nonce.ptr, other_nonce.ptr,
215 min(this_nonce.len, other_nonce.len)) < 0)
216 {
217 DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child");
218 }
219 else
220 {
221 DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child");
222 to_delete = this->child_create->get_child(this->child_create);
223 if (to_delete == NULL)
224 {
225 /* ooops, should not happen, fallback */
226 to_delete = this->child_sa;
227 }
228 }
229 }
230
231 spi = to_delete->get_spi(to_delete, TRUE);
232 protocol = to_delete->get_protocol(to_delete);
233 if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS)
234 {
235 return FAILED;
236 }
237 return SUCCESS;
238 }
239
240 /**
241 * Implementation of task_t.get_type
242 */
243 static task_type_t get_type(private_child_rekey_t *this)
244 {
245 return CHILD_REKEY;
246 }
247
248 /**
249 * Implementation of child_rekey_t.collide
250 */
251 static void collide(private_child_rekey_t *this, task_t *other)
252 {
253 DESTROY_IF(this->collision);
254 this->collision = other;
255 }
256
257 /**
258 * Implementation of task_t.migrate
259 */
260 static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
261 {
262 this->child_create->task.migrate(&this->child_create->task, ike_sa);
263 DESTROY_IF(this->collision);
264
265 this->ike_sa = ike_sa;
266 this->collision = NULL;
267 }
268
269 /**
270 * Implementation of task_t.destroy
271 */
272 static void destroy(private_child_rekey_t *this)
273 {
274 this->child_create->task.destroy(&this->child_create->task);
275 DESTROY_IF(this->collision);
276 free(this);
277 }
278
279 /*
280 * Described in header.
281 */
282 child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
283 {
284 private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
285 policy_t *policy;
286
287 this->public.collide = (void (*)(child_rekey_t*,task_t*))collide;
288 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
289 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
290 this->public.task.destroy = (void(*)(task_t*))destroy;
291 if (child_sa != NULL)
292 {
293 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
294 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
295 this->initiator = TRUE;
296 policy = child_sa->get_policy(child_sa);
297 this->child_create = child_create_create(ike_sa, policy);
298 }
299 else
300 {
301 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
302 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
303 this->initiator = FALSE;
304 this->child_create = child_create_create(ike_sa, NULL);
305 }
306
307 this->ike_sa = ike_sa;
308 this->child_sa = child_sa;
309 this->collision = NULL;
310
311 return &this->public;
312 }