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