fixed double free bug
[strongswan.git] / src / charon / sa / tasks / ike_init.c
1 /**
2 * @file ike_init.c
3 *
4 * @brief Implementation of the ike_init 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_init.h"
25
26 #include <string.h>
27
28 #include <daemon.h>
29 #include <crypto/diffie_hellman.h>
30 #include <encoding/payloads/sa_payload.h>
31 #include <encoding/payloads/ke_payload.h>
32 #include <encoding/payloads/nonce_payload.h>
33
34
35 typedef struct private_ike_init_t private_ike_init_t;
36
37 /**
38 * Private members of a ike_init_t task.
39 */
40 struct private_ike_init_t {
41
42 /**
43 * Public methods and task_t interface.
44 */
45 ike_init_t public;
46
47 /**
48 * Assigned IKE_SA.
49 */
50 ike_sa_t *ike_sa;
51
52 /**
53 * Are we the initiator?
54 */
55 bool initiator;
56
57 /**
58 * Connection established by this IKE_SA
59 */
60 connection_t *connection;
61
62 /**
63 * diffie hellman group to use
64 */
65 diffie_hellman_group_t dh_group;
66
67 /**
68 * Diffie hellman object used to generate public DH value.
69 */
70 diffie_hellman_t *diffie_hellman;
71
72 /**
73 * nonce chosen by us
74 */
75 chunk_t my_nonce;
76
77 /**
78 * nonce chosen by peer
79 */
80 chunk_t other_nonce;
81
82 /**
83 * Negotiated proposal used for IKE_SA
84 */
85 proposal_t *proposal;
86
87 /**
88 * Old IKE_SA which gets rekeyed
89 */
90 ike_sa_t *old_sa;
91 };
92
93 /**
94 * build the payloads for the message
95 */
96 static void build_payloads(private_ike_init_t *this, message_t *message)
97 {
98 sa_payload_t *sa_payload;
99 ke_payload_t *ke_payload;
100 nonce_payload_t *nonce_payload;
101 linked_list_t *proposal_list;
102 ike_sa_id_t *id;
103 proposal_t *proposal;
104 iterator_t *iterator;
105
106 id = this->ike_sa->get_id(this->ike_sa);
107
108 this->connection = this->ike_sa->get_connection(this->ike_sa);
109
110 if (this->initiator)
111 {
112 proposal_list = this->connection->get_proposals(this->connection);
113 if (this->old_sa)
114 {
115 /* include SPI of new IKE_SA when we are rekeying */
116 iterator = proposal_list->create_iterator(proposal_list, TRUE);
117 while (iterator->iterate(iterator, (void**)&proposal))
118 {
119 proposal->set_spi(proposal, id->get_initiator_spi(id));
120 }
121 iterator->destroy(iterator);
122 }
123
124 sa_payload = sa_payload_create_from_proposal_list(proposal_list);
125 proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
126 }
127 else
128 {
129 if (this->old_sa)
130 {
131 /* include SPI of new IKE_SA when we are rekeying */
132 this->proposal->set_spi(this->proposal, id->get_responder_spi(id));
133 }
134 sa_payload = sa_payload_create_from_proposal(this->proposal);
135 }
136 message->add_payload(message, (payload_t*)sa_payload);
137
138 nonce_payload = nonce_payload_create();
139 nonce_payload->set_nonce(nonce_payload, this->my_nonce);
140 message->add_payload(message, (payload_t*)nonce_payload);
141
142 ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
143 message->add_payload(message, (payload_t*)ke_payload);
144 }
145
146 /**
147 * Read payloads from message
148 */
149 static void process_payloads(private_ike_init_t *this, message_t *message)
150 {
151 iterator_t *iterator;
152 payload_t *payload;
153
154 iterator = message->get_payload_iterator(message);
155 while (iterator->iterate(iterator, (void**)&payload))
156 {
157 switch (payload->get_type(payload))
158 {
159 case SECURITY_ASSOCIATION:
160 {
161 sa_payload_t *sa_payload = (sa_payload_t*)payload;
162 linked_list_t *proposal_list;
163
164 proposal_list = sa_payload->get_proposals(sa_payload);
165 this->proposal = this->connection->select_proposal(
166 this->connection, proposal_list);
167 proposal_list->destroy_offset(proposal_list,
168 offsetof(proposal_t, destroy));
169 break;
170 }
171 case KEY_EXCHANGE:
172 {
173 ke_payload_t *ke_payload = (ke_payload_t*)payload;
174 diffie_hellman_group_t dh_group;
175 chunk_t key_data;
176
177 dh_group = ke_payload->get_dh_group_number(ke_payload);
178
179 if (this->initiator)
180 {
181 if (dh_group != this->dh_group)
182 {
183 DBG1(DBG_IKE, "received a DH group not requested (%N)",
184 diffie_hellman_group_names, dh_group);
185 break;
186 }
187 }
188 else
189 {
190 this->dh_group = dh_group;
191 if (!this->connection->check_dh_group(this->connection,
192 dh_group))
193 {
194 break;
195 }
196 this->diffie_hellman = diffie_hellman_create(dh_group);
197 }
198 if (this->diffie_hellman)
199 {
200 key_data = ke_payload->get_key_exchange_data(ke_payload);
201 this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data);
202 }
203 break;
204 }
205 case NONCE:
206 {
207 nonce_payload_t *nonce_payload = (nonce_payload_t*)payload;
208 this->other_nonce = nonce_payload->get_nonce(nonce_payload);
209 break;
210 }
211 default:
212 break;
213 }
214 }
215 iterator->destroy(iterator);
216 }
217
218 /**
219 * Implementation of task_t.process for initiator
220 */
221 static status_t build_i(private_ike_init_t *this, message_t *message)
222 {
223 randomizer_t *randomizer;
224 status_t status;
225
226 this->connection = this->ike_sa->get_connection(this->ike_sa);
227 SIG(IKE_UP_START, "initiating IKE_SA to %H",
228 this->connection->get_other_host(this->connection));
229 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
230
231 /* if the DH group is set via use_dh_group(), we already have a DH object */
232 if (!this->diffie_hellman)
233 {
234 this->dh_group = this->connection->get_dh_group(this->connection);
235 this->diffie_hellman = diffie_hellman_create(this->dh_group);
236 if (this->diffie_hellman == NULL)
237 {
238 SIG(IKE_UP_FAILED, "configured DH group %N not supported",
239 diffie_hellman_group_names, this->dh_group);
240 return FAILED;
241 }
242 }
243
244 randomizer = randomizer_create();
245 status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
246 &this->my_nonce);
247 randomizer->destroy(randomizer);
248 if (status != SUCCESS)
249 {
250 SIG(IKE_UP_FAILED, "error generating random nonce value");
251 return FAILED;
252 }
253
254 build_payloads(this, message);
255
256 return NEED_MORE;
257 }
258
259 /**
260 * Implementation of task_t.process for initiator
261 */
262 static status_t process_r(private_ike_init_t *this, message_t *message)
263 {
264 randomizer_t *randomizer;
265
266 this->connection = this->ike_sa->get_connection(this->ike_sa);
267 SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA",
268 message->get_source(message));
269 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
270
271 randomizer = randomizer_create();
272 if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
273 &this->my_nonce) != SUCCESS)
274 {
275 DBG1(DBG_IKE, "error generating random nonce value");
276 }
277 randomizer->destroy(randomizer);
278
279 process_payloads(this, message);
280
281 return NEED_MORE;
282 }
283
284 /**
285 * Implementation of task_t.build for responder
286 */
287 static status_t build_r(private_ike_init_t *this, message_t *message)
288 {
289 chunk_t secret;
290 status_t status;
291
292 /* check if we have everything we need */
293 if (this->proposal == NULL ||
294 this->other_nonce.len == 0 || this->my_nonce.len == 0)
295 {
296 SIG(IKE_UP_FAILED, "received proposals inacceptable");
297 message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
298 return FAILED;
299 }
300
301 if (this->diffie_hellman == NULL ||
302 this->diffie_hellman->get_shared_secret(this->diffie_hellman,
303 &secret) != SUCCESS)
304 {
305 chunk_t chunk;
306 u_int16_t dh_enc;
307
308 SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)",
309 diffie_hellman_group_names, this->dh_group);
310 this->dh_group = this->connection->get_dh_group(this->connection);
311 dh_enc = htons(this->dh_group);
312 chunk.ptr = (u_int8_t*)&dh_enc;
313 chunk.len = sizeof(dh_enc);
314 message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk);
315 DBG1(DBG_IKE, "requesting DH group %N",
316 diffie_hellman_group_names, this->dh_group);
317 return FAILED;
318 }
319
320
321 if (this->old_sa)
322 {
323 ike_sa_id_t *id;
324 prf_t *prf, *child_prf;
325
326 /* Apply SPI if we are rekeying */
327 id = this->ike_sa->get_id(this->ike_sa);
328 id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
329
330 /* setup crypto keys for the rekeyed SA */
331 prf = this->old_sa->get_prf(this->old_sa);
332 child_prf = this->old_sa->get_child_prf(this->old_sa);
333 status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
334 this->other_nonce, this->my_nonce,
335 FALSE, child_prf, prf);
336 }
337 else
338 {
339 /* setup crypto keys */
340 status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
341 this->other_nonce, this->my_nonce,
342 FALSE, NULL, NULL);
343 }
344 if (status != SUCCESS)
345 {
346 SIG(IKE_UP_FAILED, "key derivation failed");
347 message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
348 return FAILED;
349 }
350
351 build_payloads(this, message);
352
353 return SUCCESS;
354 }
355
356 /**
357 * Implementation of task_t.process for initiator
358 */
359 static status_t process_i(private_ike_init_t *this, message_t *message)
360 {
361 chunk_t secret;
362 status_t status;
363 iterator_t *iterator;
364 payload_t *payload;
365
366 /* check for erronous notifies */
367 iterator = message->get_payload_iterator(message);
368 while (iterator->iterate(iterator, (void**)&payload))
369 {
370 if (payload->get_type(payload) == NOTIFY)
371 {
372 notify_payload_t *notify = (notify_payload_t*)payload;
373 notify_type_t type = notify->get_notify_type(notify);
374
375 switch (type)
376 {
377 case INVALID_KE_PAYLOAD:
378 {
379 chunk_t data;
380 diffie_hellman_group_t old_dh_group;
381
382 old_dh_group = this->dh_group;
383 data = notify->get_notification_data(notify);
384 this->dh_group = ntohs(*((u_int16_t*)data.ptr));
385
386 DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested"
387 " %N", diffie_hellman_group_names, old_dh_group,
388 diffie_hellman_group_names, this->dh_group);
389 if (!this->connection->check_dh_group(this->connection,
390 this->dh_group))
391 {
392 SIG(IKE_UP_FAILED, "requested DH group %N not "
393 "acceptable, giving up", diffie_hellman_group_names,
394 this->dh_group);
395 iterator->destroy(iterator);
396 return FAILED;
397 }
398
399 this->ike_sa->reset(this->ike_sa);
400
401 iterator->destroy(iterator);
402 return NEED_MORE;
403 }
404 default:
405 {
406 if (type < 16383)
407 {
408 SIG(IKE_UP_FAILED, "received %N notify error",
409 notify_type_names, type);
410 iterator->destroy(iterator);
411 return FAILED;
412 }
413 }
414 }
415 }
416 }
417 iterator->destroy(iterator);
418
419 process_payloads(this, message);
420
421 /* check if we have everything */
422 if (this->proposal == NULL ||
423 this->other_nonce.len == 0 || this->my_nonce.len == 0)
424 {
425 SIG(IKE_UP_FAILED, "peers proposal selection invalid");
426 return FAILED;
427 }
428
429 if (this->diffie_hellman == NULL ||
430 this->diffie_hellman->get_shared_secret(this->diffie_hellman,
431 &secret) != SUCCESS)
432 {
433 SIG(IKE_UP_FAILED, "peers DH group selection invalid");
434 return FAILED;
435 }
436
437 /* Apply SPI if we are rekeying */
438 if (this->old_sa)
439 {
440 ike_sa_id_t *id;
441 prf_t *prf, *child_prf;
442
443 id = this->ike_sa->get_id(this->ike_sa);
444 id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
445
446 /* setup crypto keys for the rekeyed SA */
447 prf = this->old_sa->get_prf(this->old_sa);
448 child_prf = this->old_sa->get_child_prf(this->old_sa);
449 status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
450 this->my_nonce, this->other_nonce,
451 TRUE, child_prf, prf);
452 }
453 else
454 {
455 /* setup crypto keys for a new SA */
456 status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
457 this->my_nonce, this->other_nonce,
458 TRUE, NULL, NULL);
459 }
460 if (status != SUCCESS)
461 {
462 SIG(IKE_UP_FAILED, "key derivation failed");
463 return FAILED;
464 }
465 return SUCCESS;
466 }
467
468 /**
469 * Implementation of task_t.get_type
470 */
471 static task_type_t get_type(private_ike_init_t *this)
472 {
473 return IKE_INIT;
474 }
475
476 /**
477 * Implementation of task_t.migrate
478 */
479 static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa)
480 {
481 DESTROY_IF(this->proposal);
482 DESTROY_IF(this->diffie_hellman);
483 chunk_free(&this->my_nonce);
484 chunk_free(&this->other_nonce);
485
486 this->ike_sa = ike_sa;
487 this->proposal = NULL;
488 this->diffie_hellman = diffie_hellman_create(this->dh_group);
489 }
490
491 /**
492 * Implementation of task_t.destroy
493 */
494 static void destroy(private_ike_init_t *this)
495 {
496 DESTROY_IF(this->proposal);
497 DESTROY_IF(this->diffie_hellman);
498 chunk_free(&this->my_nonce);
499 chunk_free(&this->other_nonce);
500 free(this);
501 }
502
503 /*
504 * Described in header.
505 */
506 ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
507 {
508 private_ike_init_t *this = malloc_thing(private_ike_init_t);
509
510 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
511 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
512 this->public.task.destroy = (void(*)(task_t*))destroy;
513 if (initiator)
514 {
515 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
516 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
517 }
518 else
519 {
520 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
521 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
522 }
523
524 this->ike_sa = ike_sa;
525 this->initiator = initiator;
526 this->dh_group = MODP_NONE;
527 this->diffie_hellman = NULL;
528 this->my_nonce = chunk_empty;
529 this->other_nonce = chunk_empty;
530 this->proposal = NULL;
531 this->connection = NULL;
532 this->old_sa = old_sa;
533
534 return &this->public;
535 }