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