Derive IKE keys as IKEv1 initiator, too
[strongswan.git] / src / libcharon / sa / tasks / main_mode.c
1 /*
2 * Copyright (C) 2011 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2011 Martin Willi
6 * Copyright (C) 2011 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "main_mode.h"
20
21 #include <string.h>
22
23 #include <daemon.h>
24 #include <sa/keymat_v1.h>
25 #include <crypto/diffie_hellman.h>
26 #include <encoding/payloads/sa_payload.h>
27 #include <encoding/payloads/ke_payload.h>
28 #include <encoding/payloads/nonce_payload.h>
29 #include <encoding/payloads/id_payload.h>
30
31 typedef struct private_main_mode_t private_main_mode_t;
32
33 /**
34 * Private members of a main_mode_t task.
35 */
36 struct private_main_mode_t {
37
38 /**
39 * Public methods and task_t interface.
40 */
41 main_mode_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 * IKE config to establish
55 */
56 ike_cfg_t *ike_cfg;
57
58 /**
59 * Peer config to use
60 */
61 peer_cfg_t *peer_cfg;
62
63 /**
64 * Local authentication configuration
65 */
66 auth_cfg_t *my_auth;
67
68 /**
69 * Remote authentication configuration
70 */
71 auth_cfg_t *other_auth;
72
73 /**
74 * selected IKE proposal
75 */
76 proposal_t *proposal;
77
78 /**
79 * DH exchange
80 */
81 diffie_hellman_t *dh;
82
83 /**
84 * Keymat derivation (from SA)
85 */
86 keymat_v1_t *keymat;
87
88 /**
89 * Received public DH value from peer
90 */
91 chunk_t dh_value;
92
93 /**
94 * Initiators nonce
95 */
96 chunk_t nonce_i;
97
98 /**
99 * Responder nonce
100 */
101 chunk_t nonce_r;
102
103 /** states of main mode */
104 enum {
105 MM_INIT,
106 MM_SA,
107 MM_KE,
108 MM_AUTH,
109 } state;
110 };
111
112 /**
113 * Get the first authentcation config from peer config
114 */
115 static auth_cfg_t *get_auth_cfg(private_main_mode_t *this, bool local)
116 {
117 enumerator_t *enumerator;
118 auth_cfg_t *cfg = NULL;
119
120 enumerator = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg,
121 local);
122 enumerator->enumerate(enumerator, &cfg);
123 enumerator->destroy(enumerator);
124 return cfg;
125 }
126
127 METHOD(task_t, build_i, status_t,
128 private_main_mode_t *this, message_t *message)
129 {
130 switch (this->state)
131 {
132 case MM_INIT:
133 {
134 sa_payload_t *sa_payload;
135 linked_list_t *proposals;
136
137 this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
138 DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H",
139 this->ike_sa->get_name(this->ike_sa),
140 this->ike_sa->get_unique_id(this->ike_sa),
141 this->ike_sa->get_other_host(this->ike_sa));
142 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
143
144 proposals = this->ike_cfg->get_proposals(this->ike_cfg);
145
146 sa_payload = sa_payload_create_from_proposal_list(
147 SECURITY_ASSOCIATION_V1, proposals);
148 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
149
150 message->add_payload(message, &sa_payload->payload_interface);
151
152 this->state = MM_SA;
153 return NEED_MORE;
154 }
155 case MM_SA:
156 {
157 ke_payload_t *ke_payload;
158 nonce_payload_t *nonce_payload;
159 u_int16_t group;
160 rng_t *rng;
161
162 if (!this->proposal->get_algorithm(this->proposal,
163 DIFFIE_HELLMAN_GROUP, &group, NULL))
164 {
165 DBG1(DBG_IKE, "DH group selection failed");
166 return FAILED;
167 }
168 this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
169 group);
170 if (!this->dh)
171 {
172 DBG1(DBG_IKE, "negotiated DH group not supported");
173 return FAILED;
174 }
175 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
176 this->dh);
177 message->add_payload(message, &ke_payload->payload_interface);
178
179 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
180 if (!rng)
181 {
182 DBG1(DBG_IKE, "no RNG found to create nonce");
183 return FAILED;
184 }
185 rng->allocate_bytes(rng, NONCE_SIZE, &this->nonce_i);
186 rng->destroy(rng);
187
188 nonce_payload = nonce_payload_create(NONCE_V1);
189 nonce_payload->set_nonce(nonce_payload, this->nonce_i);
190 message->add_payload(message, &nonce_payload->payload_interface);
191
192 this->state = MM_KE;
193 return NEED_MORE;
194 }
195 case MM_KE:
196 {
197 id_payload_t *id_payload;
198 identification_t *id;
199
200 this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
201 this->peer_cfg->get_ref(this->peer_cfg);
202
203 this->my_auth = get_auth_cfg(this, TRUE);
204 this->other_auth = get_auth_cfg(this, FALSE);
205 if (!this->my_auth || !this->other_auth)
206 {
207 DBG1(DBG_CFG, "no auth config found");
208 return FAILED;
209 }
210 id = this->my_auth->get(this->my_auth, AUTH_RULE_IDENTITY);
211 if (!id)
212 {
213 DBG1(DBG_CFG, "own identity not known");
214 return FAILED;
215 }
216
217 this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
218
219 id_payload = id_payload_create_from_identification(ID_V1, id);
220 message->add_payload(message, &id_payload->payload_interface);
221
222 /* TODO-IKEv1: authenticate */
223
224 this->state = MM_AUTH;
225 return NEED_MORE;
226 }
227 default:
228 return FAILED;
229 }
230 }
231
232 METHOD(task_t, process_r, status_t,
233 private_main_mode_t *this, message_t *message)
234 {
235 switch (this->state)
236 {
237 case MM_INIT:
238 {
239
240 linked_list_t *list;
241 sa_payload_t *sa_payload;
242
243 this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
244 DBG0(DBG_IKE, "%H is initiating a Main Mode",
245 message->get_source(message));
246 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
247
248 this->ike_sa->update_hosts(this->ike_sa,
249 message->get_destination(message),
250 message->get_source(message), TRUE);
251
252 sa_payload = (sa_payload_t*)message->get_payload(message,
253 SECURITY_ASSOCIATION_V1);
254 if (!sa_payload)
255 {
256 DBG1(DBG_IKE, "SA payload missing");
257 return FAILED;
258 }
259 list = sa_payload->get_proposals(sa_payload);
260 this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
261 list, FALSE);
262 list->destroy_offset(list, offsetof(proposal_t, destroy));
263 if (!this->proposal)
264 {
265 DBG1(DBG_IKE, "no proposal found");
266 return FAILED;
267 }
268 this->state = MM_SA;
269 return NEED_MORE;
270 }
271 case MM_SA:
272 {
273 ke_payload_t *ke_payload;
274 nonce_payload_t *nonce_payload;
275 u_int16_t group;
276
277 ke_payload = (ke_payload_t*)message->get_payload(message,
278 KEY_EXCHANGE_V1);
279 if (!ke_payload)
280 {
281 DBG1(DBG_IKE, "KE payload missing");
282 return FAILED;
283 }
284 this->dh_value = ke_payload->get_key_exchange_data(ke_payload);
285 this->dh_value = chunk_clone(this->dh_value);
286
287 if (!this->proposal->get_algorithm(this->proposal,
288 DIFFIE_HELLMAN_GROUP, &group, NULL))
289 {
290 DBG1(DBG_IKE, "DH group selection failed");
291 return FAILED;
292 }
293 this->dh = lib->crypto->create_dh(lib->crypto, group);
294 if (!this->dh)
295 {
296 DBG1(DBG_IKE, "negotiated DH group not supported");
297 return FAILED;
298 }
299 this->dh->set_other_public_value(this->dh, this->dh_value);
300
301
302 nonce_payload = (nonce_payload_t*)message->get_payload(message,
303 NONCE_V1);
304 if (!nonce_payload)
305 {
306 DBG1(DBG_IKE, "Nonce payload missing");
307 return FAILED;
308 }
309 this->nonce_i = nonce_payload->get_nonce(nonce_payload);
310
311 this->state = MM_KE;
312 return NEED_MORE;
313 }
314 case MM_KE:
315 {
316 enumerator_t *enumerator;
317 id_payload_t *id_payload;
318 identification_t *id, *any;
319
320 id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
321 if (!id_payload)
322 {
323 DBG1(DBG_IKE, "IDii payload missing");
324 return FAILED;
325 }
326
327 id = id_payload->get_identification(id_payload);
328 any = identification_create_from_encoding(ID_ANY, chunk_empty);
329 enumerator = charon->backends->create_peer_cfg_enumerator(
330 charon->backends,
331 this->ike_sa->get_my_host(this->ike_sa),
332 this->ike_sa->get_other_host(this->ike_sa),
333 any, id);
334 if (!enumerator->enumerate(enumerator, &this->peer_cfg))
335 {
336 DBG1(DBG_IKE, "no peer config found");
337 id->destroy(id);
338 any->destroy(any);
339 enumerator->destroy(enumerator);
340 return FAILED;
341 }
342 this->peer_cfg->get_ref(this->peer_cfg);
343 enumerator->destroy(enumerator);
344 any->destroy(any);
345
346 this->ike_sa->set_other_id(this->ike_sa, id);
347
348 this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
349
350 this->my_auth = get_auth_cfg(this, TRUE);
351 this->other_auth = get_auth_cfg(this, FALSE);
352 if (!this->my_auth || !this->other_auth)
353 {
354 DBG1(DBG_CFG, "auth config missing");
355 return FAILED;
356 }
357
358 /* TODO-IKEv1: authenticate peer */
359
360 this->state = MM_AUTH;
361 return NEED_MORE;
362 }
363 default:
364 return FAILED;
365 }
366 }
367
368 /**
369 * Lookup a shared secret for this IKE_SA
370 */
371 static shared_key_t *lookup_shared_key(private_main_mode_t *this)
372 {
373 host_t *me, *other;
374 identification_t *my_id, *other_id;
375 shared_key_t *shared_key;
376
377 me = this->ike_sa->get_my_host(this->ike_sa);
378 other = this->ike_sa->get_other_host(this->ike_sa);
379 my_id = identification_create_from_sockaddr(me->get_sockaddr(me));
380 other_id = identification_create_from_sockaddr(other->get_sockaddr(other));
381 if (!my_id || !other_id)
382 {
383 DESTROY_IF(my_id);
384 DESTROY_IF(other_id);
385 return NULL;
386 }
387 shared_key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE, my_id,
388 other_id);
389 if (!shared_key)
390 {
391 DBG1(DBG_IKE, "no shared key found for %H - %H", me, other);
392 }
393 my_id->destroy(my_id);
394 other_id->destroy(other_id);
395 return shared_key;
396 }
397
398 /**
399 * Derive key material for this IKE_SA
400 */
401 static bool derive_keys(private_main_mode_t *this, chunk_t nonce_i,
402 chunk_t nonce_r)
403 {
404 ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa);
405 shared_key_t *shared_key = NULL;
406 auth_class_t auth;
407
408 /* TODO-IKEv1: support other authentication classes */
409 auth = AUTH_CLASS_PSK;
410 switch (auth)
411 {
412 case AUTH_CLASS_PSK:
413 shared_key = lookup_shared_key(this);
414 break;
415 default:
416 break;
417 }
418 if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh,
419 this->dh_value, nonce_i, nonce_r, id, auth, shared_key))
420 {
421 DESTROY_IF(shared_key);
422 return FALSE;
423 }
424 DESTROY_IF(shared_key);
425 charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, nonce_i, nonce_r,
426 NULL);
427 return TRUE;
428 }
429
430 METHOD(task_t, build_r, status_t,
431 private_main_mode_t *this, message_t *message)
432 {
433 switch (this->state)
434 {
435 case MM_SA:
436 {
437 sa_payload_t *sa_payload;
438
439 sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1,
440 this->proposal);
441 message->add_payload(message, &sa_payload->payload_interface);
442 return NEED_MORE;
443 }
444 case MM_KE:
445 {
446 ke_payload_t *ke_payload;
447 nonce_payload_t *nonce_payload;
448 rng_t *rng;
449
450 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
451 this->dh);
452 message->add_payload(message, &ke_payload->payload_interface);
453
454 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
455 if (!rng)
456 {
457 DBG1(DBG_IKE, "no RNG found to create nonce");
458 return FAILED;
459 }
460 rng->allocate_bytes(rng, NONCE_SIZE, &this->nonce_r);
461 rng->destroy(rng);
462
463 if (!derive_keys(this, this->nonce_i, this->nonce_r))
464 {
465 DBG1(DBG_IKE, "key derivation failed");
466 return FAILED;
467 }
468
469 nonce_payload = nonce_payload_create(NONCE_V1);
470 nonce_payload->set_nonce(nonce_payload, this->nonce_r);
471 message->add_payload(message, &nonce_payload->payload_interface);
472 return NEED_MORE;
473 }
474 case MM_AUTH:
475 {
476 id_payload_t *id_payload;
477 identification_t *id;
478
479 id = this->my_auth->get(this->my_auth, AUTH_RULE_IDENTITY);
480 if (!id)
481 {
482 DBG1(DBG_CFG, "own identity not known");
483 return FAILED;
484 }
485
486 this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
487
488 id_payload = id_payload_create_from_identification(ID_V1, id);
489 message->add_payload(message, &id_payload->payload_interface);
490
491 /* TODO-IKEv1: authenticate us */
492
493 /* TODO-IKEv1: check for XAUTH rounds, queue them */
494 DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
495 this->ike_sa->get_name(this->ike_sa),
496 this->ike_sa->get_unique_id(this->ike_sa),
497 this->ike_sa->get_my_host(this->ike_sa),
498 this->ike_sa->get_my_id(this->ike_sa),
499 this->ike_sa->get_other_host(this->ike_sa),
500 this->ike_sa->get_other_id(this->ike_sa));
501 this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
502 charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
503 return SUCCESS;
504 }
505 default:
506 return FAILED;
507 }
508 }
509
510 METHOD(task_t, process_i, status_t,
511 private_main_mode_t *this, message_t *message)
512 {
513 switch (this->state)
514 {
515 case MM_SA:
516 {
517 linked_list_t *list;
518 sa_payload_t *sa_payload;
519
520 sa_payload = (sa_payload_t*)message->get_payload(message,
521 SECURITY_ASSOCIATION_V1);
522 if (!sa_payload)
523 {
524 DBG1(DBG_IKE, "SA payload missing");
525 return FAILED;
526 }
527 list = sa_payload->get_proposals(sa_payload);
528 this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
529 list, FALSE);
530 list->destroy_offset(list, offsetof(proposal_t, destroy));
531 if (!this->proposal)
532 {
533 DBG1(DBG_IKE, "no proposal found");
534 return FAILED;
535 }
536 return NEED_MORE;
537 }
538 case MM_KE:
539 {
540 ke_payload_t *ke_payload;
541 nonce_payload_t *nonce_payload;
542
543 ke_payload = (ke_payload_t*)message->get_payload(message,
544 KEY_EXCHANGE_V1);
545 if (!ke_payload)
546 {
547 DBG1(DBG_IKE, "KE payload missing");
548 return FAILED;
549 }
550 this->dh_value = ke_payload->get_key_exchange_data(ke_payload);
551 this->dh_value = chunk_clone(this->dh_value);
552 this->dh->set_other_public_value(this->dh, this->dh_value);
553
554 nonce_payload = (nonce_payload_t*)message->get_payload(message,
555 NONCE_V1);
556 if (!nonce_payload)
557 {
558 DBG1(DBG_IKE, "Nonce payload missing");
559 return FAILED;
560 }
561 this->nonce_r = nonce_payload->get_nonce(nonce_payload);
562
563 if (!derive_keys(this, this->nonce_i, this->nonce_r))
564 {
565 DBG1(DBG_IKE, "key derivation failed");
566 return FAILED;
567 }
568
569 return NEED_MORE;
570 }
571 case MM_AUTH:
572 {
573 id_payload_t *id_payload;
574 identification_t *id;
575
576 id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
577 if (!id_payload)
578 {
579 DBG1(DBG_IKE, "IDir payload missing");
580 return FAILED;
581 }
582 id = id_payload->get_identification(id_payload);
583 if (!id->matches(id, this->other_auth->get(this->other_auth,
584 AUTH_RULE_IDENTITY)))
585 {
586 DBG1(DBG_IKE, "IDir does not match");
587 id->destroy(id);
588 return FAILED;
589 }
590 this->ike_sa->set_other_id(this->ike_sa, id);
591
592 /* TODO-IKEv1: verify auth */
593
594 /* TODO-IKEv1: check for XAUTH rounds, queue them */
595 DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
596 this->ike_sa->get_name(this->ike_sa),
597 this->ike_sa->get_unique_id(this->ike_sa),
598 this->ike_sa->get_my_host(this->ike_sa),
599 this->ike_sa->get_my_id(this->ike_sa),
600 this->ike_sa->get_other_host(this->ike_sa),
601 this->ike_sa->get_other_id(this->ike_sa));
602 this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
603 charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
604 return SUCCESS;
605 }
606 default:
607 return FAILED;
608 }
609 }
610
611 METHOD(task_t, get_type, task_type_t,
612 private_main_mode_t *this)
613 {
614 return TASK_MAIN_MODE;
615 }
616
617 METHOD(task_t, migrate, void,
618 private_main_mode_t *this, ike_sa_t *ike_sa)
619 {
620 this->ike_sa = ike_sa;
621 }
622
623 METHOD(task_t, destroy, void,
624 private_main_mode_t *this)
625 {
626 DESTROY_IF(this->peer_cfg);
627 DESTROY_IF(this->proposal);
628 DESTROY_IF(this->dh);
629 free(this->dh_value.ptr);
630 free(this->nonce_i.ptr);
631 free(this->nonce_r.ptr);
632 free(this);
633 }
634
635 /*
636 * Described in header.
637 */
638 main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator)
639 {
640 private_main_mode_t *this;
641
642 INIT(this,
643 .public = {
644 .task = {
645 .get_type = _get_type,
646 .migrate = _migrate,
647 .destroy = _destroy,
648 },
649 },
650 .ike_sa = ike_sa,
651 .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
652 .initiator = initiator,
653 .state = MM_INIT,
654 );
655
656 if (initiator)
657 {
658 this->public.task.build = _build_i;
659 this->public.task.process = _process_i;
660 }
661 else
662 {
663 this->public.task.build = _build_r;
664 this->public.task.process = _process_r;
665 }
666
667 return &this->public;
668 }