Add support for KE payloads in IKEv1 quick mode (PFS)
[strongswan.git] / src / libcharon / sa / tasks / quick_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 "quick_mode.h"
17
18 #include <string.h>
19
20 #include <daemon.h>
21 #include <sa/keymat_v1.h>
22 #include <encoding/payloads/sa_payload.h>
23 #include <encoding/payloads/nonce_payload.h>
24 #include <encoding/payloads/ke_payload.h>
25 #include <encoding/payloads/id_payload.h>
26 #include <encoding/payloads/payload.h>
27
28 typedef struct private_quick_mode_t private_quick_mode_t;
29
30 /**
31 * Private members of a quick_mode_t task.
32 */
33 struct private_quick_mode_t {
34
35 /**
36 * Public methods and task_t interface.
37 */
38 quick_mode_t public;
39
40 /**
41 * Assigned IKE_SA.
42 */
43 ike_sa_t *ike_sa;
44
45 /**
46 * TRUE if we are initiating quick mode
47 */
48 bool initiator;
49
50 /**
51 * Traffic selector of initiator
52 */
53 traffic_selector_t *tsi;
54
55 /**
56 * Traffic selector of responder
57 */
58 traffic_selector_t *tsr;
59
60 /**
61 * Initiators nonce
62 */
63 chunk_t nonce_i;
64
65 /**
66 * Responder nonce
67 */
68 chunk_t nonce_r;
69
70 /**
71 * Initiators ESP SPI
72 */
73 u_int32_t spi_i;
74
75 /**
76 * Responder ESP SPI
77 */
78 u_int32_t spi_r;
79
80 /**
81 * selected CHILD_SA proposal
82 */
83 proposal_t *proposal;
84
85 /**
86 * Config of CHILD_SA to establish
87 */
88 child_cfg_t *config;
89
90 /**
91 * CHILD_SA we are about to establish
92 */
93 child_sa_t *child_sa;
94
95 /**
96 * IKEv1 keymat
97 */
98 keymat_v1_t *keymat;
99
100 /**
101 * DH exchange, when PFS is in use
102 */
103 diffie_hellman_t *dh;
104
105 /**
106 * Negotiated lifetime of new SA
107 */
108 u_int32_t lifetime;
109
110 /**
111 * Negotaited lifebytes of new SA
112 */
113 u_int64_t lifebytes;
114
115 /** states of quick mode */
116 enum {
117 QM_INIT,
118 QM_NEGOTIATED,
119 } state;
120 };
121
122 /**
123 * Install negotiated CHILD_SA
124 */
125 static bool install(private_quick_mode_t *this)
126 {
127 status_t status, status_i, status_o;
128 chunk_t encr_i, encr_r, integ_i, integ_r;
129 linked_list_t *tsi, *tsr;
130
131 this->child_sa->set_proposal(this->child_sa, this->proposal);
132 this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
133 this->child_sa->set_mode(this->child_sa, MODE_TUNNEL);
134 this->child_sa->set_protocol(this->child_sa,
135 this->proposal->get_protocol(this->proposal));
136
137 status_i = status_o = FAILED;
138 encr_i = encr_r = integ_i = integ_r = chunk_empty;
139 tsi = linked_list_create();
140 tsr = linked_list_create();
141 tsi->insert_last(tsi, this->tsi);
142 tsr->insert_last(tsr, this->tsr);
143 if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh,
144 this->spi_i, this->spi_r, this->nonce_i, this->nonce_r,
145 &encr_i, &integ_i, &encr_r, &integ_r))
146 {
147 if (this->initiator)
148 {
149 status_i = this->child_sa->install(this->child_sa, encr_r, integ_r,
150 this->spi_i, 0, TRUE, FALSE, tsi, tsr);
151 status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
152 this->spi_r, 0, FALSE, FALSE, tsi, tsr);
153 }
154 else
155 {
156 status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
157 this->spi_r, 0, TRUE, FALSE, tsr, tsi);
158 status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
159 this->spi_i, 0, FALSE, FALSE, tsr, tsi);
160 }
161 }
162 chunk_clear(&integ_i);
163 chunk_clear(&integ_r);
164 chunk_clear(&encr_i);
165 chunk_clear(&encr_r);
166
167 if (status_i != SUCCESS || status_o != SUCCESS)
168 {
169 DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel",
170 (status_i != SUCCESS) ? "inbound " : "",
171 (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "",
172 (status_o != SUCCESS) ? "outbound " : "");
173 tsi->destroy(tsi);
174 tsr->destroy(tsr);
175 return FALSE;
176 }
177
178 if (this->initiator)
179 {
180 status = this->child_sa->add_policies(this->child_sa, tsi, tsr);
181 }
182 else
183 {
184 status = this->child_sa->add_policies(this->child_sa, tsr, tsi);
185 }
186 tsi->destroy(tsi);
187 tsr->destroy(tsr);
188 if (status != SUCCESS)
189 {
190 DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
191 return FALSE;
192 }
193
194 charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
195 this->dh, this->nonce_i, this->nonce_r);
196
197 /* add to IKE_SA, and remove from task */
198 this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
199 this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
200
201 DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
202 "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
203 this->child_sa->get_name(this->child_sa),
204 this->child_sa->get_reqid(this->child_sa),
205 ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
206 ntohl(this->child_sa->get_spi(this->child_sa, FALSE)),
207 this->child_sa->get_traffic_selectors(this->child_sa, TRUE),
208 this->child_sa->get_traffic_selectors(this->child_sa, FALSE));
209
210 charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
211
212 this->child_sa = NULL;
213
214 return TRUE;
215 }
216
217 /**
218 * Generate and add NONCE
219 */
220 static bool add_nonce(private_quick_mode_t *this, chunk_t *nonce,
221 message_t *message)
222 {
223 nonce_payload_t *nonce_payload;
224 rng_t *rng;
225
226 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
227 if (!rng)
228 {
229 DBG1(DBG_IKE, "no RNG found to create nonce");
230 return FALSE;
231 }
232 rng->allocate_bytes(rng, NONCE_SIZE, nonce);
233 rng->destroy(rng);
234
235 nonce_payload = nonce_payload_create(NONCE_V1);
236 nonce_payload->set_nonce(nonce_payload, *nonce);
237 message->add_payload(message, &nonce_payload->payload_interface);
238
239 return TRUE;
240 }
241
242 /**
243 * Extract nonce from NONCE payload
244 */
245 static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce,
246 message_t *message)
247 {
248 nonce_payload_t *nonce_payload;
249
250 nonce_payload = (nonce_payload_t*)message->get_payload(message, NONCE_V1);
251 if (!nonce_payload)
252 {
253 DBG1(DBG_IKE, "NONCE payload missing in message");
254 return FALSE;
255 }
256 *nonce = nonce_payload->get_nonce(nonce_payload);
257
258 return TRUE;
259 }
260
261 /**
262 * Add KE payload to message
263 */
264 static void add_ke(private_quick_mode_t *this, message_t *message)
265 {
266 ke_payload_t *ke_payload;
267
268 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1, this->dh);
269 message->add_payload(message, &ke_payload->payload_interface);
270 }
271
272 /**
273 * Get DH value from a KE payload
274 */
275 static bool get_ke(private_quick_mode_t *this, message_t *message)
276 {
277 ke_payload_t *ke_payload;
278
279 ke_payload = (ke_payload_t*)message->get_payload(message, KEY_EXCHANGE_V1);
280 if (!ke_payload)
281 {
282 DBG1(DBG_IKE, "KE payload missing");
283 return FALSE;
284 }
285 this->dh->set_other_public_value(this->dh,
286 ke_payload->get_key_exchange_data(ke_payload));
287 return TRUE;
288 }
289
290 /**
291 * Select a traffic selector from configuration
292 */
293 static traffic_selector_t* select_ts(private_quick_mode_t *this, bool initiator)
294 {
295 traffic_selector_t *ts;
296 linked_list_t *list;
297 host_t *host;
298
299 if (initiator)
300 {
301 host = this->ike_sa->get_my_host(this->ike_sa);
302 }
303 else
304 {
305 host = this->ike_sa->get_other_host(this->ike_sa);
306 }
307 list = this->config->get_traffic_selectors(this->config, initiator,
308 NULL, host);
309 if (list->get_first(list, (void**)&ts) == SUCCESS)
310 {
311 if (list->get_count(list) > 1)
312 {
313 DBG1(DBG_IKE, "configuration has more than one %s traffic selector,"
314 " using first only", initiator ? "initiator" : "responder");
315 }
316 ts = ts->clone(ts);
317 }
318 else
319 {
320 DBG1(DBG_IKE, "%s traffic selector missing in configuration",
321 initiator ? "initiator" : "responder");
322 ts = NULL;
323 }
324 list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
325 return ts;
326 }
327
328 /**
329 * Add selected traffic selectors to message
330 */
331 static void add_ts(private_quick_mode_t *this, message_t *message)
332 {
333 id_payload_t *id_payload;
334 host_t *hsi, *hsr;
335
336 if (this->initiator)
337 {
338 hsi = this->ike_sa->get_my_host(this->ike_sa);
339 hsr = this->ike_sa->get_other_host(this->ike_sa);
340 }
341 else
342 {
343 hsr = this->ike_sa->get_my_host(this->ike_sa);
344 hsi = this->ike_sa->get_other_host(this->ike_sa);
345 }
346 /* add ID payload only if negotiating non host2host tunnels */
347 if (!this->tsi->is_host(this->tsi, hsi) ||
348 !this->tsr->is_host(this->tsr, hsr) ||
349 this->tsi->get_protocol(this->tsi) ||
350 this->tsr->get_protocol(this->tsr) ||
351 this->tsi->get_from_port(this->tsi) ||
352 this->tsr->get_from_port(this->tsr) ||
353 this->tsi->get_to_port(this->tsi) != 65535 ||
354 this->tsr->get_to_port(this->tsr) != 65535)
355 {
356 id_payload = id_payload_create_from_ts(this->tsi);
357 message->add_payload(message, &id_payload->payload_interface);
358 id_payload = id_payload_create_from_ts(this->tsr);
359 message->add_payload(message, &id_payload->payload_interface);
360 }
361 }
362
363 /**
364 * Get traffic selectors from received message
365 */
366 static bool get_ts(private_quick_mode_t *this, message_t *message)
367 {
368 traffic_selector_t *tsi = NULL, *tsr = NULL;
369 enumerator_t *enumerator;
370 id_payload_t *id_payload;
371 payload_t *payload;
372 host_t *hsi, *hsr;
373 bool first = TRUE;
374
375 enumerator = message->create_payload_enumerator(message);
376 while (enumerator->enumerate(enumerator, &payload))
377 {
378 if (payload->get_type(payload) == ID_V1)
379 {
380 id_payload = (id_payload_t*)payload;
381
382 if (first)
383 {
384 tsi = id_payload->get_ts(id_payload);
385 first = FALSE;
386 }
387 else
388 {
389 tsr = id_payload->get_ts(id_payload);
390 break;
391 }
392 }
393 }
394 enumerator->destroy(enumerator);
395
396 /* create host2host selectors if ID payloads missing */
397 if (this->initiator)
398 {
399 hsi = this->ike_sa->get_my_host(this->ike_sa);
400 hsr = this->ike_sa->get_other_host(this->ike_sa);
401 }
402 else
403 {
404 hsr = this->ike_sa->get_my_host(this->ike_sa);
405 hsi = this->ike_sa->get_other_host(this->ike_sa);
406 }
407 if (!tsi)
408 {
409 tsi = traffic_selector_create_from_subnet(hsi->clone(hsi),
410 hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0);
411 }
412 if (!tsr)
413 {
414 tsr = traffic_selector_create_from_subnet(hsr->clone(hsr),
415 hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0);
416 }
417 if (this->initiator)
418 {
419 /* check if peer selection valid */
420 if (!tsr->is_contained_in(tsr, this->tsr) ||
421 !tsi->is_contained_in(tsi, this->tsi))
422 {
423 DBG1(DBG_IKE, "peer selected invalid traffic selectors: ",
424 "%R for %R, %R for %R", tsi, this->tsi, tsr, this->tsr);
425 tsi->destroy(tsi);
426 tsr->destroy(tsr);
427 return FALSE;
428 }
429 this->tsi->destroy(this->tsi);
430 this->tsr->destroy(this->tsr);
431 this->tsi = tsi;
432 this->tsr = tsr;
433 }
434 else
435 {
436 this->tsi = tsi;
437 this->tsr = tsr;
438 }
439 return TRUE;
440 }
441
442 /**
443 * Add NAT-OA payloads
444 */
445 static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
446 {
447 identification_t *id;
448 id_payload_t *nat_oa;
449 host_t *src, *dst;
450
451 src = message->get_source(message);
452 dst = message->get_destination(message);
453
454 src = this->initiator ? src : dst;
455 dst = this->initiator ? dst : src;
456
457 /* first NAT-OA is the initiator's address */
458 id = identification_create_from_sockaddr(src->get_sockaddr(src));
459 nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
460 message->add_payload(message, (payload_t*)nat_oa);
461 id->destroy(id);
462
463 /* second NAT-OA is that of the responder */
464 id = identification_create_from_sockaddr(dst->get_sockaddr(dst));
465 nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
466 message->add_payload(message, (payload_t*)nat_oa);
467 id->destroy(id);
468 }
469
470 /**
471 * Look up lifetimes
472 */
473 static void get_lifetimes(private_quick_mode_t *this)
474 {
475 lifetime_cfg_t *lft;
476
477 lft = this->config->get_lifetime(this->config);
478 if (lft->time.life)
479 {
480 this->lifetime = lft->time.life;
481 }
482 else if (lft->bytes.life)
483 {
484 this->lifebytes = lft->bytes.life;
485 }
486 free(lft);
487 }
488
489 /**
490 * Check and apply lifetimes
491 */
492 static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload)
493 {
494 u_int32_t lifetime;
495 u_int64_t lifebytes;
496
497 lifetime = sa_payload->get_lifetime(sa_payload);
498 lifebytes = sa_payload->get_lifebytes(sa_payload);
499 if (this->lifetime != lifetime)
500 {
501 DBG1(DBG_IKE, "received %us lifetime, configured %us, using lower",
502 lifetime, this->lifetime);
503 this->lifetime = min(this->lifetime, lifetime);
504 }
505 if (this->lifebytes != lifebytes)
506 {
507 DBG1(DBG_IKE, "received %llu lifebytes, configured %llu, using lower",
508 lifebytes, this->lifebytes);
509 this->lifebytes = min(this->lifebytes, lifebytes);
510 }
511 }
512
513 METHOD(task_t, build_i, status_t,
514 private_quick_mode_t *this, message_t *message)
515 {
516 switch (this->state)
517 {
518 case QM_INIT:
519 {
520 enumerator_t *enumerator;
521 sa_payload_t *sa_payload;
522 linked_list_t *list;
523 proposal_t *proposal;
524 ipsec_mode_t mode;
525 diffie_hellman_group_t group;
526 bool udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
527
528 this->child_sa = child_sa_create(
529 this->ike_sa->get_my_host(this->ike_sa),
530 this->ike_sa->get_other_host(this->ike_sa),
531 this->config, 0, udp);
532
533 list = this->config->get_proposals(this->config, FALSE);
534
535 this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
536 if (!this->spi_i)
537 {
538 DBG1(DBG_IKE, "allocating SPI from kernel failed");
539 return FAILED;
540 }
541 enumerator = list->create_enumerator(list);
542 while (enumerator->enumerate(enumerator, &proposal))
543 {
544 proposal->set_spi(proposal, this->spi_i);
545 }
546 enumerator->destroy(enumerator);
547
548 mode = this->config->get_mode(this->config);
549 if (udp && mode == MODE_TRANSPORT)
550 {
551 /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
552 add_nat_oa_payloads(this, message);
553 }
554
555 get_lifetimes(this);
556 sa_payload = sa_payload_create_from_proposals_v1(list,
557 this->lifetime, this->lifebytes, AUTH_NONE,
558 mode, udp);
559 list->destroy_offset(list, offsetof(proposal_t, destroy));
560 message->add_payload(message, &sa_payload->payload_interface);
561
562 if (!add_nonce(this, &this->nonce_i, message))
563 {
564 return FAILED;
565 }
566
567 group = this->config->get_dh_group(this->config);
568 if (group != MODP_NONE)
569 {
570 this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
571 group);
572 if (!this->dh)
573 {
574 DBG1(DBG_IKE, "configured DH group %N not supported",
575 diffie_hellman_group_names, group);
576 return FAILED;
577 }
578 add_ke(this, message);
579 }
580 this->tsi = select_ts(this, TRUE);
581 this->tsr = select_ts(this, FALSE);
582 if (!this->tsi || !this->tsr)
583 {
584 return FAILED;
585 }
586 add_ts(this, message);
587 return NEED_MORE;
588 }
589 case QM_NEGOTIATED:
590 {
591 return SUCCESS;
592 }
593 default:
594 return FAILED;
595 }
596 }
597
598 status_t process_notify(notify_payload_t *notify)
599 {
600 if(notify->get_notify_type(notify) < 16384)
601 {
602 DBG1(DBG_IKE, "Received %N error notification.", notify_type_names, notify->get_notify_type(notify));
603 return FAILED;
604 }
605 DBG1(DBG_IKE, "Received %N notification.", notify_type_names, notify->get_notify_type(notify));
606 return SUCCESS;
607 }
608
609 METHOD(task_t, process_r, status_t,
610 private_quick_mode_t *this, message_t *message)
611 {
612 enumerator_t *enumerator;
613 payload_t *payload;
614 status_t status;
615
616 switch (this->state)
617 {
618 case QM_INIT:
619 {
620 sa_payload_t *sa_payload;
621 linked_list_t *tsi, *tsr, *list;
622 peer_cfg_t *peer_cfg;
623 host_t *me, *other;
624 u_int16_t group;
625 bool udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
626
627 if (!get_ts(this, message))
628 {
629 return FAILED;
630 }
631 me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
632 if (!me)
633 {
634 me = this->ike_sa->get_my_host(this->ike_sa);
635 }
636 other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
637 if (!other)
638 {
639 other = this->ike_sa->get_other_host(this->ike_sa);
640 }
641 peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
642 tsi = linked_list_create();
643 tsr = linked_list_create();
644 tsi->insert_last(tsi, this->tsi);
645 tsr->insert_last(tsr, this->tsr);
646 this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
647 me, other);
648 tsi->destroy(tsi);
649 tsr->destroy(tsr);
650 if (!this->config)
651 {
652 DBG1(DBG_IKE, "no child config found");
653 return FAILED;
654 }
655
656 sa_payload = (sa_payload_t*)message->get_payload(message,
657 SECURITY_ASSOCIATION_V1);
658 if (!sa_payload)
659 {
660 DBG1(DBG_IKE, "sa payload missing");
661 return FAILED;
662 }
663 list = sa_payload->get_proposals(sa_payload);
664 this->proposal = this->config->select_proposal(this->config,
665 list, FALSE, FALSE);
666 list->destroy_offset(list, offsetof(proposal_t, destroy));
667
668 get_lifetimes(this);
669 apply_lifetimes(this, sa_payload);
670
671 if (!this->proposal)
672 {
673 DBG1(DBG_IKE, "no matching proposal found");
674 return FAILED;
675 }
676 this->spi_i = this->proposal->get_spi(this->proposal);
677
678 if (!get_nonce(this, &this->nonce_i, message))
679 {
680 return FAILED;
681 }
682
683 if (this->proposal->get_algorithm(this->proposal,
684 DIFFIE_HELLMAN_GROUP, &group, NULL))
685 {
686 this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
687 group);
688 if (!this->dh)
689 {
690 DBG1(DBG_IKE, "negotiated DH group %N not supported",
691 diffie_hellman_group_names, group);
692 return FAILED;
693 }
694 if (!get_ke(this, message))
695 {
696 return FAILED;
697 }
698 }
699
700 this->child_sa = child_sa_create(
701 this->ike_sa->get_my_host(this->ike_sa),
702 this->ike_sa->get_other_host(this->ike_sa),
703 this->config, 0, udp);
704 return NEED_MORE;
705 }
706 case QM_NEGOTIATED:
707 {
708 enumerator = message->create_payload_enumerator(message);
709 while(enumerator->enumerate(enumerator, &payload))
710 {
711 if(payload->get_type(payload) == NOTIFY_V1)
712 {
713 status = process_notify((notify_payload_t *)payload);
714 if(status != SUCCESS)
715 {
716 return status;
717 }
718 }
719 }
720 enumerator->destroy(enumerator);
721 if (!install(this))
722 {
723 return FAILED;
724 }
725 return SUCCESS;
726 }
727 default:
728 return FAILED;
729 }
730 }
731
732 METHOD(task_t, build_r, status_t,
733 private_quick_mode_t *this, message_t *message)
734 {
735 switch (this->state)
736 {
737 case QM_INIT:
738 {
739 sa_payload_t *sa_payload;
740 ipsec_mode_t mode;
741 bool udp = this->child_sa->has_encap(this->child_sa);
742
743 this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
744 if (!this->spi_r)
745 {
746 DBG1(DBG_IKE, "allocating SPI from kernel failed");
747 return FAILED;
748 }
749 this->proposal->set_spi(this->proposal, this->spi_r);
750
751 mode = this->config->get_mode(this->config);
752 if (udp && mode == MODE_TRANSPORT)
753 {
754 /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
755 add_nat_oa_payloads(this, message);
756 }
757
758 sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
759 this->lifetime, this->lifebytes, AUTH_NONE,
760 mode, udp);
761 message->add_payload(message, &sa_payload->payload_interface);
762
763 if (!add_nonce(this, &this->nonce_r, message))
764 {
765 return FAILED;
766 }
767 if (this->dh)
768 {
769 add_ke(this, message);
770 }
771
772 add_ts(this, message);
773
774 this->state = QM_NEGOTIATED;
775 return NEED_MORE;
776 }
777 default:
778 return FAILED;
779 }
780 }
781
782 METHOD(task_t, process_i, status_t,
783 private_quick_mode_t *this, message_t *message)
784 {
785 switch (this->state)
786 {
787 case QM_INIT:
788 {
789 sa_payload_t *sa_payload;
790 linked_list_t *list;
791
792 sa_payload = (sa_payload_t*)message->get_payload(message,
793 SECURITY_ASSOCIATION_V1);
794 if (!sa_payload)
795 {
796 DBG1(DBG_IKE, "sa payload missing");
797 return FAILED;
798 }
799 list = sa_payload->get_proposals(sa_payload);
800 this->proposal = this->config->select_proposal(this->config,
801 list, FALSE, FALSE);
802 list->destroy_offset(list, offsetof(proposal_t, destroy));
803 if (!this->proposal)
804 {
805 DBG1(DBG_IKE, "no matching proposal found");
806 return FAILED;
807 }
808 this->spi_r = this->proposal->get_spi(this->proposal);
809
810 apply_lifetimes(this, sa_payload);
811
812 if (!get_nonce(this, &this->nonce_r, message))
813 {
814 return FAILED;
815 }
816 if (this->dh && !get_ke(this, message))
817 {
818 return FAILED;
819 }
820 if (!get_ts(this, message))
821 {
822 return FAILED;
823 }
824 if (!install(this))
825 {
826 return FAILED;
827 }
828 this->state = QM_NEGOTIATED;
829 return NEED_MORE;
830 }
831 default:
832 return FAILED;
833 }
834 }
835
836 METHOD(task_t, get_type, task_type_t,
837 private_quick_mode_t *this)
838 {
839 return TASK_QUICK_MODE;
840 }
841
842 METHOD(task_t, migrate, void,
843 private_quick_mode_t *this, ike_sa_t *ike_sa)
844 {
845 this->ike_sa = ike_sa;
846 }
847
848 METHOD(task_t, destroy, void,
849 private_quick_mode_t *this)
850 {
851 chunk_free(&this->nonce_i);
852 chunk_free(&this->nonce_r);
853 DESTROY_IF(this->tsi);
854 DESTROY_IF(this->tsr);
855 DESTROY_IF(this->proposal);
856 DESTROY_IF(this->child_sa);
857 DESTROY_IF(this->config);
858 DESTROY_IF(this->dh);
859 free(this);
860 }
861
862 /*
863 * Described in header.
864 */
865 quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
866 traffic_selector_t *tsi, traffic_selector_t *tsr)
867 {
868 private_quick_mode_t *this;
869
870 INIT(this,
871 .public = {
872 .task = {
873 .get_type = _get_type,
874 .migrate = _migrate,
875 .destroy = _destroy,
876 },
877 },
878 .ike_sa = ike_sa,
879 .initiator = config != NULL,
880 .config = config,
881 .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
882 .state = QM_INIT,
883 );
884
885 if (config)
886 {
887 this->public.task.build = _build_i;
888 this->public.task.process = _process_i;
889 }
890 else
891 {
892 this->public.task.build = _build_r;
893 this->public.task.process = _process_r;
894 }
895
896 return &this->public;
897 }