fixed SPI byte order
[strongswan.git] / src / charon / sa / tasks / ike_rekey.c
1 /**
2 * @file ike_rekey.c
3 *
4 * @brief Implementation of the ike_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 "ike_rekey.h"
25
26 #include <daemon.h>
27 #include <encoding/payloads/notify_payload.h>
28 #include <sa/tasks/ike_init.h>
29 #include <queues/jobs/delete_ike_sa_job.h>
30
31
32 typedef struct private_ike_rekey_t private_ike_rekey_t;
33
34 /**
35 * Private members of a ike_rekey_t task.
36 */
37 struct private_ike_rekey_t {
38
39 /**
40 * Public methods and task_t interface.
41 */
42 ike_rekey_t public;
43
44 /**
45 * Assigned IKE_SA.
46 */
47 ike_sa_t *ike_sa;
48
49 /**
50 * New IKE_SA which replaces the current one
51 */
52 ike_sa_t *new_sa;
53
54 /**
55 * Are we the initiator?
56 */
57 bool initiator;
58
59 /**
60 * the IKE_INIT task which is reused to simplify rekeying
61 */
62 ike_init_t *ike_init;
63
64 /**
65 * colliding task detected by the task manager
66 */
67 task_t *collision;
68 };
69
70 /**
71 * Implementation of task_t.build for initiator
72 */
73 static status_t build_i(private_ike_rekey_t *this, message_t *message)
74 {
75 connection_t *connection;
76 policy_t *policy;
77 ike_sa_id_t *id;
78
79 id = ike_sa_id_create(0, 0, TRUE);
80 this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
81 id->destroy(id);
82
83 connection = this->ike_sa->get_connection(this->ike_sa);
84 policy = this->ike_sa->get_policy(this->ike_sa);
85 this->new_sa->set_connection(this->new_sa, connection);
86 this->new_sa->set_policy(this->new_sa, policy);
87
88 this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa);
89 this->ike_init->task.build(&this->ike_init->task, message);
90
91 this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
92
93 return NEED_MORE;
94 }
95
96 /**
97 * Implementation of task_t.process for initiator
98 */
99 static status_t process_r(private_ike_rekey_t *this, message_t *message)
100 {
101 connection_t *connection;
102 policy_t *policy;
103 ike_sa_id_t *id;
104 iterator_t *iterator;
105 child_sa_t *child_sa;
106
107 if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
108 {
109 DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting");
110 return NEED_MORE;
111 }
112
113 iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
114 while (iterator->iterate(iterator, (void**)&child_sa))
115 {
116 switch (child_sa->get_state(child_sa))
117 {
118 case CHILD_CREATED:
119 case CHILD_REKEYING:
120 case CHILD_DELETING:
121 /* we do not allow rekeying while we have children in-progress */
122 DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
123 iterator->destroy(iterator);
124 return NEED_MORE;
125 default:
126 break;
127 }
128 }
129 iterator->destroy(iterator);
130
131 id = ike_sa_id_create(0, 0, FALSE);
132 this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
133 id->destroy(id);
134
135 connection = this->ike_sa->get_connection(this->ike_sa);
136 policy = this->ike_sa->get_policy(this->ike_sa);
137 this->new_sa->set_connection(this->new_sa, connection);
138 this->new_sa->set_policy(this->new_sa, policy);
139
140 this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa);
141 this->ike_init->task.process(&this->ike_init->task, 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_ike_rekey_t *this, message_t *message)
150 {
151 if (this->new_sa == NULL)
152 {
153 /* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */
154 message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
155 return SUCCESS;
156 }
157
158 if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED)
159 {
160 return SUCCESS;
161 }
162
163 this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
164 this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
165
166 return SUCCESS;
167 }
168
169 /**
170 * Implementation of task_t.process for initiator
171 */
172 static status_t process_i(private_ike_rekey_t *this, message_t *message)
173 {
174 job_t *job;
175 ike_sa_id_t *to_delete;
176
177 if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED)
178 {
179 /* rekeying failed, fallback to old SA */
180 if (!(this->collision &&
181 this->collision->get_type(this->collision) == IKE_DELETE))
182 {
183 this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
184 /* TODO: reschedule rekeying */
185 }
186 return SUCCESS;
187 }
188
189 this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
190 to_delete = this->ike_sa->get_id(this->ike_sa);
191
192 /* check for collisions */
193 if (this->collision &&
194 this->collision->get_type(this->collision) == IKE_REKEY)
195 {
196 chunk_t this_nonce, other_nonce;
197 host_t *host;
198 private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision;
199
200 this_nonce = this->ike_init->get_lower_nonce(this->ike_init);
201 other_nonce = other->ike_init->get_lower_nonce(other->ike_init);
202
203 /* if we have the lower nonce, delete rekeyed SA. If not, delete
204 * the redundant. */
205 if (memcmp(this_nonce.ptr, other_nonce.ptr,
206 min(this_nonce.len, other_nonce.len)) < 0)
207 {
208 DBG1(DBG_IKE, "IKE_SA rekey collision won, deleting rekeyed IKE_SA");
209 charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa);
210 }
211 else
212 {
213 DBG1(DBG_IKE, "IKE_SA rekey collision lost, deleting redundant IKE_SA");
214 /* apply host for a proper delete */
215 host = this->ike_sa->get_my_host(this->ike_sa);
216 this->new_sa->set_my_host(this->new_sa, host->clone(host));
217 host = this->ike_sa->get_other_host(this->ike_sa);
218 this->new_sa->set_other_host(this->new_sa, host->clone(host));
219 this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
220 to_delete = this->new_sa->get_id(this->new_sa);
221 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
222 /* inherit to other->new_sa in destroy() */
223 this->new_sa = other->new_sa;
224 other->new_sa = NULL;
225 }
226 }
227
228 job = (job_t*)delete_ike_sa_job_create(to_delete, TRUE);
229 charon->job_queue->add(charon->job_queue, job);
230
231 return SUCCESS;
232 }
233
234 /**
235 * Implementation of task_t.get_type
236 */
237 static task_type_t get_type(private_ike_rekey_t *this)
238 {
239 return IKE_REKEY;
240 }
241
242 static void collide(private_ike_rekey_t* this, task_t *other)
243 {
244 DESTROY_IF(this->collision);
245 this->collision = other;
246 }
247
248 /**
249 * Implementation of task_t.migrate
250 */
251 static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa)
252 {
253 if (this->ike_init)
254 {
255 this->ike_init->task.destroy(&this->ike_init->task);
256 }
257 if (this->new_sa)
258 {
259 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
260 this->new_sa);
261 }
262 DESTROY_IF(this->collision);
263
264 this->collision = NULL;
265 this->ike_sa = ike_sa;
266 this->new_sa = NULL;
267 this->ike_init = NULL;
268 }
269
270 /**
271 * Implementation of task_t.destroy
272 */
273 static void destroy(private_ike_rekey_t *this)
274 {
275 if (this->new_sa)
276 {
277 if (this->new_sa->get_state(this->new_sa) == IKE_ESTABLISHED)
278 {
279 this->new_sa->inherit(this->new_sa, this->ike_sa);
280 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
281 }
282 else
283 {
284 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
285 this->new_sa);
286 }
287 }
288 if (this->ike_init)
289 {
290 this->ike_init->task.destroy(&this->ike_init->task);
291 }
292 DESTROY_IF(this->collision);
293 free(this);
294 }
295
296 /*
297 * Described in header.
298 */
299 ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator)
300 {
301 private_ike_rekey_t *this = malloc_thing(private_ike_rekey_t);
302
303 this->public.collide = (void(*)(ike_rekey_t*,task_t*))collide;
304 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
305 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
306 this->public.task.destroy = (void(*)(task_t*))destroy;
307 if (initiator)
308 {
309 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
310 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
311 }
312 else
313 {
314 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
315 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
316 }
317
318 this->ike_sa = ike_sa;
319 this->new_sa = NULL;
320 this->ike_init = NULL;
321 this->initiator = initiator;
322 this->collision = NULL;
323
324 return &this->public;
325 }