Fixed reference counting bugs in main mode
[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 enumerator->destroy(enumerator);
115 return cfg;
116 }
117
118 METHOD(task_t, build_i, status_t,
119 private_main_mode_t *this, message_t *message)
120 {
121 switch (this->state)
122 {
123 case MM_INIT:
124 {
125 sa_payload_t *sa_payload;
126 linked_list_t *proposals;
127
128 this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
129 DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H",
130 this->ike_sa->get_name(this->ike_sa),
131 this->ike_sa->get_unique_id(this->ike_sa),
132 this->ike_sa->get_other_host(this->ike_sa));
133 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
134
135 proposals = this->ike_cfg->get_proposals(this->ike_cfg);
136
137 sa_payload = sa_payload_create_from_proposal_list(
138 SECURITY_ASSOCIATION_V1, proposals);
139 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
140
141 message->add_payload(message, &sa_payload->payload_interface);
142
143 this->state = MM_SA;
144 return NEED_MORE;
145 }
146 case MM_SA:
147 {
148 ke_payload_t *ke_payload;
149 nonce_payload_t *nonce_payload;
150 u_int16_t group;
151 rng_t *rng;
152
153 if (!this->proposal->get_algorithm(this->proposal,
154 DIFFIE_HELLMAN_GROUP, &group, NULL))
155 {
156 DBG1(DBG_IKE, "DH group selection failed");
157 return FAILED;
158 }
159 this->dh = lib->crypto->create_dh(lib->crypto, group);
160 if (!this->dh)
161 {
162 DBG1(DBG_IKE, "negotiated DH group not supported");
163 return FAILED;
164 }
165 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
166 this->dh);
167 message->add_payload(message, &ke_payload->payload_interface);
168
169 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
170 if (!rng)
171 {
172 DBG1(DBG_IKE, "no RNG found to create nonce");
173 return FAILED;
174 }
175 rng->allocate_bytes(rng, NONCE_SIZE, &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
301 this->state = MM_KE;
302 return NEED_MORE;
303 }
304 case MM_KE:
305 {
306 enumerator_t *enumerator;
307 id_payload_t *id_payload;
308 identification_t *id, *any;
309
310 id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
311 if (!id_payload)
312 {
313 DBG1(DBG_IKE, "IDii payload missing");
314 return FAILED;
315 }
316
317 id = id_payload->get_identification(id_payload);
318 any = identification_create_from_encoding(ID_ANY, chunk_empty);
319 enumerator = charon->backends->create_peer_cfg_enumerator(
320 charon->backends,
321 this->ike_sa->get_my_host(this->ike_sa),
322 this->ike_sa->get_other_host(this->ike_sa),
323 any, id);
324 if (!enumerator->enumerate(enumerator, &this->peer_cfg))
325 {
326 DBG1(DBG_IKE, "no peer config found");
327 id->destroy(id);
328 any->destroy(any);
329 enumerator->destroy(enumerator);
330 return FAILED;
331 }
332 this->peer_cfg->get_ref(this->peer_cfg);
333 enumerator->destroy(enumerator);
334 any->destroy(any);
335
336 this->ike_sa->set_other_id(this->ike_sa, id);
337
338 this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
339
340 this->my_auth = get_auth_cfg(this, TRUE);
341 this->other_auth = get_auth_cfg(this, FALSE);
342 if (!this->my_auth || !this->other_auth)
343 {
344 DBG1(DBG_CFG, "auth config missing");
345 return FAILED;
346 }
347
348 /* TODO-IKEv1: authenticate peer */
349
350 this->state = MM_AUTH;
351 return NEED_MORE;
352 }
353 default:
354 return FAILED;
355 }
356 }
357
358 METHOD(task_t, build_r, status_t,
359 private_main_mode_t *this, message_t *message)
360 {
361 switch (this->state)
362 {
363 case MM_SA:
364 {
365 sa_payload_t *sa_payload;
366
367 sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1,
368 this->proposal);
369 message->add_payload(message, &sa_payload->payload_interface);
370 return NEED_MORE;
371 }
372 case MM_KE:
373 {
374 ke_payload_t *ke_payload;
375 nonce_payload_t *nonce_payload;
376 rng_t *rng;
377
378 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
379 this->dh);
380 message->add_payload(message, &ke_payload->payload_interface);
381
382 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
383 if (!rng)
384 {
385 DBG1(DBG_IKE, "no RNG found to create nonce");
386 return FAILED;
387 }
388 rng->allocate_bytes(rng, NONCE_SIZE, &this->nonce_r);
389 rng->destroy(rng);
390
391 nonce_payload = nonce_payload_create(NONCE_V1);
392 nonce_payload->set_nonce(nonce_payload, this->nonce_r);
393 message->add_payload(message, &nonce_payload->payload_interface);
394 return NEED_MORE;
395 }
396 case MM_AUTH:
397 {
398 id_payload_t *id_payload;
399 identification_t *id;
400
401 id = this->my_auth->get(this->my_auth, AUTH_RULE_IDENTITY);
402 if (!id)
403 {
404 DBG1(DBG_CFG, "own identity not known");
405 return FAILED;
406 }
407
408 this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
409
410 id_payload = id_payload_create_from_identification(ID_V1, id);
411 message->add_payload(message, &id_payload->payload_interface);
412
413 /* TODO-IKEv1: authenticate us */
414
415 /* TODO-IKEv1: check for XAUTH rounds, queue them */
416 DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
417 this->ike_sa->get_name(this->ike_sa),
418 this->ike_sa->get_unique_id(this->ike_sa),
419 this->ike_sa->get_my_host(this->ike_sa),
420 this->ike_sa->get_my_id(this->ike_sa),
421 this->ike_sa->get_other_host(this->ike_sa),
422 this->ike_sa->get_other_id(this->ike_sa));
423 this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
424 charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
425 return SUCCESS;
426 }
427 default:
428 return FAILED;
429 }
430 }
431
432 METHOD(task_t, process_i, status_t,
433 private_main_mode_t *this, message_t *message)
434 {
435 switch (this->state)
436 {
437 case MM_SA:
438 {
439 linked_list_t *list;
440 sa_payload_t *sa_payload;
441
442 sa_payload = (sa_payload_t*)message->get_payload(message,
443 SECURITY_ASSOCIATION_V1);
444 if (!sa_payload)
445 {
446 DBG1(DBG_IKE, "SA payload missing");
447 return FAILED;
448 }
449 list = sa_payload->get_proposals(sa_payload);
450 this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
451 list, FALSE);
452 list->destroy_offset(list, offsetof(proposal_t, destroy));
453 if (!this->proposal)
454 {
455 DBG1(DBG_IKE, "no proposal found");
456 return FAILED;
457 }
458 return NEED_MORE;
459 }
460 case MM_KE:
461 {
462 ke_payload_t *ke_payload;
463 nonce_payload_t *nonce_payload;
464
465 ke_payload = (ke_payload_t*)message->get_payload(message,
466 KEY_EXCHANGE_V1);
467 if (!ke_payload)
468 {
469 DBG1(DBG_IKE, "KE payload missing");
470 return FAILED;
471 }
472 this->dh_value = ke_payload->get_key_exchange_data(ke_payload);
473 this->dh_value = chunk_clone(this->dh_value);
474 this->dh->set_other_public_value(this->dh, this->dh_value);
475
476 nonce_payload = (nonce_payload_t*)message->get_payload(message,
477 NONCE_V1);
478 if (!nonce_payload)
479 {
480 DBG1(DBG_IKE, "Nonce payload missing");
481 return FAILED;
482 }
483 this->nonce_r = nonce_payload->get_nonce(nonce_payload);
484
485 return NEED_MORE;
486 }
487 case MM_AUTH:
488 {
489 id_payload_t *id_payload;
490 identification_t *id;
491
492 id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
493 if (!id_payload)
494 {
495 DBG1(DBG_IKE, "IDir payload missing");
496 return FAILED;
497 }
498 id = id_payload->get_identification(id_payload);
499 if (!id->matches(id, this->other_auth->get(this->other_auth,
500 AUTH_RULE_IDENTITY)))
501 {
502 DBG1(DBG_IKE, "IDir does not match");
503 id->destroy(id);
504 return FAILED;
505 }
506 this->ike_sa->set_other_id(this->ike_sa, id);
507
508 /* TODO-IKEv1: verify auth */
509
510 /* TODO-IKEv1: check for XAUTH rounds, queue them */
511 DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
512 this->ike_sa->get_name(this->ike_sa),
513 this->ike_sa->get_unique_id(this->ike_sa),
514 this->ike_sa->get_my_host(this->ike_sa),
515 this->ike_sa->get_my_id(this->ike_sa),
516 this->ike_sa->get_other_host(this->ike_sa),
517 this->ike_sa->get_other_id(this->ike_sa));
518 this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
519 charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
520 return SUCCESS;
521 }
522 default:
523 return FAILED;
524 }
525 }
526
527 METHOD(task_t, get_type, task_type_t,
528 private_main_mode_t *this)
529 {
530 return MAIN_MODE;
531 }
532
533 METHOD(task_t, migrate, void,
534 private_main_mode_t *this, ike_sa_t *ike_sa)
535 {
536 this->ike_sa = ike_sa;
537 }
538
539 METHOD(task_t, destroy, void,
540 private_main_mode_t *this)
541 {
542 DESTROY_IF(this->peer_cfg);
543 DESTROY_IF(this->proposal);
544 DESTROY_IF(this->dh);
545 free(this->dh_value.ptr);
546 free(this->nonce_i.ptr);
547 free(this->nonce_r.ptr);
548 free(this);
549 }
550
551 /*
552 * Described in header.
553 */
554 main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator)
555 {
556 private_main_mode_t *this;
557
558 INIT(this,
559 .public = {
560 .task = {
561 .get_type = _get_type,
562 .migrate = _migrate,
563 .destroy = _destroy,
564 },
565 },
566 .ike_sa = ike_sa,
567 .initiator = initiator,
568 .state = MM_INIT,
569 );
570
571 if (initiator)
572 {
573 this->public.task.build = _build_i;
574 this->public.task.process = _process_i;
575 }
576 else
577 {
578 this->public.task.build = _build_r;
579 this->public.task.process = _process_r;
580 }
581
582 return &this->public;
583 }