]>
Commit | Line | Data |
---|---|---|
2b04aa46 | 1 | /* |
4de361d9 | 2 | * Copyright (C) 2012-2015 Tobias Brunner |
47b448b8 TB |
3 | * Hochschule fuer Technik Rapperswil |
4 | * | |
2b04aa46 MW |
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 | ||
0ff8d20a VR |
19 | /* |
20 | * Copyright (C) 2012 Volker RĂ¼melin | |
21 | * | |
22 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
23 | * of this software and associated documentation files (the "Software"), to deal | |
24 | * in the Software without restriction, including without limitation the rights | |
25 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
26 | * copies of the Software, and to permit persons to whom the Software is | |
27 | * furnished to do so, subject to the following conditions: | |
28 | * | |
29 | * The above copyright notice and this permission notice shall be included in | |
30 | * all copies or substantial portions of the Software. | |
31 | * | |
32 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
33 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
34 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
35 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
36 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
37 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
38 | * THE SOFTWARE. | |
39 | */ | |
40 | ||
2b04aa46 MW |
41 | #include "quick_mode.h" |
42 | ||
43 | #include <string.h> | |
44 | ||
45 | #include <daemon.h> | |
15a682f4 | 46 | #include <sa/ikev1/keymat_v1.h> |
21b7db99 MW |
47 | #include <encoding/payloads/sa_payload.h> |
48 | #include <encoding/payloads/nonce_payload.h> | |
5adf855e | 49 | #include <encoding/payloads/ke_payload.h> |
21b7db99 | 50 | #include <encoding/payloads/id_payload.h> |
07abb470 | 51 | #include <encoding/payloads/payload.h> |
15a682f4 MW |
52 | #include <sa/ikev1/tasks/informational.h> |
53 | #include <sa/ikev1/tasks/quick_delete.h> | |
c60246a6 | 54 | #include <processing/jobs/inactivity_job.h> |
2b04aa46 MW |
55 | |
56 | typedef struct private_quick_mode_t private_quick_mode_t; | |
57 | ||
58 | /** | |
59 | * Private members of a quick_mode_t task. | |
60 | */ | |
61 | struct private_quick_mode_t { | |
62 | ||
63 | /** | |
64 | * Public methods and task_t interface. | |
65 | */ | |
66 | quick_mode_t public; | |
67 | ||
68 | /** | |
69 | * Assigned IKE_SA. | |
70 | */ | |
71 | ike_sa_t *ike_sa; | |
72 | ||
5351d63c MW |
73 | /** |
74 | * TRUE if we are initiating quick mode | |
75 | */ | |
76 | bool initiator; | |
77 | ||
2b04aa46 MW |
78 | /** |
79 | * Traffic selector of initiator | |
80 | */ | |
81 | traffic_selector_t *tsi; | |
82 | ||
83 | /** | |
84 | * Traffic selector of responder | |
85 | */ | |
86 | traffic_selector_t *tsr; | |
87 | ||
88 | /** | |
89 | * Initiators nonce | |
90 | */ | |
91 | chunk_t nonce_i; | |
92 | ||
93 | /** | |
94 | * Responder nonce | |
95 | */ | |
96 | chunk_t nonce_r; | |
97 | ||
5351d63c MW |
98 | /** |
99 | * Initiators ESP SPI | |
100 | */ | |
b12c53ce | 101 | uint32_t spi_i; |
5351d63c MW |
102 | |
103 | /** | |
104 | * Responder ESP SPI | |
105 | */ | |
b12c53ce | 106 | uint32_t spi_r; |
5351d63c | 107 | |
47b448b8 TB |
108 | /** |
109 | * Initiators IPComp CPI | |
110 | */ | |
b12c53ce | 111 | uint16_t cpi_i; |
47b448b8 TB |
112 | |
113 | /** | |
114 | * Responders IPComp CPI | |
115 | */ | |
b12c53ce | 116 | uint16_t cpi_r; |
47b448b8 | 117 | |
2b04aa46 MW |
118 | /** |
119 | * selected CHILD_SA proposal | |
120 | */ | |
121 | proposal_t *proposal; | |
122 | ||
123 | /** | |
124 | * Config of CHILD_SA to establish | |
125 | */ | |
126 | child_cfg_t *config; | |
127 | ||
128 | /** | |
129 | * CHILD_SA we are about to establish | |
130 | */ | |
131 | child_sa_t *child_sa; | |
132 | ||
5351d63c MW |
133 | /** |
134 | * IKEv1 keymat | |
135 | */ | |
136 | keymat_v1_t *keymat; | |
137 | ||
5adf855e MW |
138 | /** |
139 | * DH exchange, when PFS is in use | |
140 | */ | |
141 | diffie_hellman_t *dh; | |
142 | ||
cd0017d4 MW |
143 | /** |
144 | * Negotiated lifetime of new SA | |
145 | */ | |
b12c53ce | 146 | uint32_t lifetime; |
cd0017d4 MW |
147 | |
148 | /** | |
9e01d7ca | 149 | * Negotiated lifebytes of new SA |
cd0017d4 | 150 | */ |
b12c53ce | 151 | uint64_t lifebytes; |
cd0017d4 | 152 | |
14dc7941 MW |
153 | /** |
154 | * Reqid to use, 0 for auto-allocate | |
155 | */ | |
b12c53ce | 156 | uint32_t reqid; |
14dc7941 | 157 | |
85b23888 MW |
158 | /** |
159 | * Explicit inbound mark value to use, if any | |
160 | */ | |
161 | u_int mark_in; | |
162 | ||
163 | /** | |
164 | * Explicit inbound mark value to use, if any | |
165 | */ | |
166 | u_int mark_out; | |
167 | ||
669d8bde MW |
168 | /** |
169 | * SPI of SA we rekey | |
170 | */ | |
b12c53ce | 171 | uint32_t rekey; |
669d8bde | 172 | |
2f3c08d2 TB |
173 | /** |
174 | * Delete old child after successful rekey | |
175 | */ | |
176 | bool delete; | |
177 | ||
90731f38 MW |
178 | /** |
179 | * Negotiated mode, tunnel or transport | |
180 | */ | |
181 | ipsec_mode_t mode; | |
182 | ||
908fe163 MW |
183 | /* |
184 | * SA protocol (ESP|AH) negotiated | |
185 | */ | |
186 | protocol_id_t proto; | |
187 | ||
7fd7ffc6 MW |
188 | /** |
189 | * Use UDP encapsulation | |
190 | */ | |
191 | bool udp; | |
192 | ||
4de361d9 TB |
193 | /** |
194 | * Message ID of handled quick mode exchange | |
195 | */ | |
b12c53ce | 196 | uint32_t mid; |
4de361d9 | 197 | |
2b04aa46 MW |
198 | /** states of quick mode */ |
199 | enum { | |
200 | QM_INIT, | |
21b7db99 | 201 | QM_NEGOTIATED, |
2b04aa46 MW |
202 | } state; |
203 | }; | |
204 | ||
c60246a6 MW |
205 | /** |
206 | * Schedule inactivity timeout for CHILD_SA with reqid, if enabled | |
207 | */ | |
208 | static void schedule_inactivity_timeout(private_quick_mode_t *this) | |
209 | { | |
b12c53ce | 210 | uint32_t timeout; |
c60246a6 MW |
211 | bool close_ike; |
212 | ||
213 | timeout = this->config->get_inactivity(this->config); | |
214 | if (timeout) | |
215 | { | |
216 | close_ike = lib->settings->get_bool(lib->settings, | |
d223fe80 | 217 | "%s.inactivity_close_ike", FALSE, lib->ns); |
c60246a6 | 218 | lib->scheduler->schedule_job(lib->scheduler, (job_t*) |
85ace154 MW |
219 | inactivity_job_create(this->child_sa->get_unique_id(this->child_sa), |
220 | timeout, close_ike), timeout); | |
c60246a6 MW |
221 | } |
222 | } | |
223 | ||
c4acf375 MW |
224 | /** |
225 | * Check if we have a an address pool configured | |
226 | */ | |
227 | static bool have_pool(ike_sa_t *ike_sa) | |
228 | { | |
229 | enumerator_t *enumerator; | |
230 | peer_cfg_t *peer_cfg; | |
231 | char *pool; | |
232 | bool found = FALSE; | |
233 | ||
234 | peer_cfg = ike_sa->get_peer_cfg(ike_sa); | |
235 | if (peer_cfg) | |
236 | { | |
237 | enumerator = peer_cfg->create_pool_enumerator(peer_cfg); | |
238 | if (enumerator->enumerate(enumerator, &pool)) | |
239 | { | |
240 | found = TRUE; | |
241 | } | |
242 | enumerator->destroy(enumerator); | |
243 | } | |
244 | return found; | |
245 | } | |
246 | ||
247 | /** | |
7ee37114 | 248 | * Get hosts to use for dynamic traffic selectors |
c4acf375 | 249 | */ |
7ee37114 | 250 | static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local) |
c4acf375 MW |
251 | { |
252 | enumerator_t *enumerator; | |
7ee37114 | 253 | linked_list_t *list; |
c4acf375 MW |
254 | host_t *host; |
255 | ||
7ee37114 | 256 | list = linked_list_create(); |
c4acf375 | 257 | enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); |
7ee37114 | 258 | while (enumerator->enumerate(enumerator, &host)) |
c4acf375 | 259 | { |
7ee37114 MW |
260 | list->insert_last(list, host); |
261 | } | |
262 | enumerator->destroy(enumerator); | |
263 | ||
264 | if (list->get_count(list) == 0) | |
265 | { /* no virtual IPs assigned */ | |
c4acf375 MW |
266 | if (local) |
267 | { | |
268 | host = ike_sa->get_my_host(ike_sa); | |
7ee37114 | 269 | list->insert_last(list, host); |
c4acf375 | 270 | } |
7ee37114 MW |
271 | else if (!have_pool(ike_sa)) |
272 | { /* use host only if we don't have a pool configured */ | |
273 | host = ike_sa->get_other_host(ike_sa); | |
274 | list->insert_last(list, host); | |
c4acf375 MW |
275 | } |
276 | } | |
7ee37114 | 277 | return list; |
c4acf375 MW |
278 | } |
279 | ||
5351d63c MW |
280 | /** |
281 | * Install negotiated CHILD_SA | |
282 | */ | |
283 | static bool install(private_quick_mode_t *this) | |
284 | { | |
285 | status_t status, status_i, status_o; | |
286 | chunk_t encr_i, encr_r, integ_i, integ_r; | |
553bb787 | 287 | linked_list_t *tsi, *tsr, *my_ts, *other_ts; |
669d8bde | 288 | child_sa_t *old = NULL; |
5351d63c MW |
289 | |
290 | this->child_sa->set_proposal(this->child_sa, this->proposal); | |
291 | this->child_sa->set_state(this->child_sa, CHILD_INSTALLING); | |
90731f38 | 292 | this->child_sa->set_mode(this->child_sa, this->mode); |
47b448b8 TB |
293 | |
294 | if (this->cpi_i && this->cpi_r) | |
295 | { /* DEFLATE is the only transform we currently support */ | |
296 | this->child_sa->set_ipcomp(this->child_sa, IPCOMP_DEFLATE); | |
297 | } | |
298 | else | |
299 | { | |
300 | this->cpi_i = this->cpi_r = 0; | |
301 | } | |
302 | ||
5351d63c MW |
303 | this->child_sa->set_protocol(this->child_sa, |
304 | this->proposal->get_protocol(this->proposal)); | |
305 | ||
306 | status_i = status_o = FAILED; | |
307 | encr_i = encr_r = integ_i = integ_r = chunk_empty; | |
abdb82fc MW |
308 | tsi = linked_list_create_with_items(this->tsi->clone(this->tsi), NULL); |
309 | tsr = linked_list_create_with_items(this->tsr->clone(this->tsr), NULL); | |
696fa8e0 MW |
310 | if (this->initiator) |
311 | { | |
312 | charon->bus->narrow(charon->bus, this->child_sa, | |
313 | NARROW_INITIATOR_POST_AUTH, tsi, tsr); | |
314 | } | |
315 | else | |
316 | { | |
317 | charon->bus->narrow(charon->bus, this->child_sa, | |
f942588f | 318 | NARROW_RESPONDER_POST, tsr, tsi); |
696fa8e0 MW |
319 | } |
320 | if (tsi->get_count(tsi) == 0 || tsr->get_count(tsr) == 0) | |
321 | { | |
322 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
323 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
324 | DBG1(DBG_IKE, "no acceptable traffic selectors found"); | |
325 | return FALSE; | |
326 | } | |
327 | ||
5adf855e | 328 | if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh, |
a7910b1c MW |
329 | this->spi_i, this->spi_r, this->nonce_i, this->nonce_r, |
330 | &encr_i, &integ_i, &encr_r, &integ_r)) | |
5351d63c MW |
331 | { |
332 | if (this->initiator) | |
333 | { | |
a8c94544 MW |
334 | status_i = this->child_sa->install(this->child_sa, |
335 | encr_r, integ_r, this->spi_i, this->cpi_i, | |
336 | this->initiator, TRUE, FALSE, tsi, tsr); | |
337 | status_o = this->child_sa->install(this->child_sa, | |
338 | encr_i, integ_i, this->spi_r, this->cpi_r, | |
339 | this->initiator, FALSE, FALSE, tsi, tsr); | |
5351d63c MW |
340 | } |
341 | else | |
342 | { | |
a8c94544 MW |
343 | status_i = this->child_sa->install(this->child_sa, |
344 | encr_i, integ_i, this->spi_r, this->cpi_r, | |
345 | this->initiator, TRUE, FALSE, tsr, tsi); | |
346 | status_o = this->child_sa->install(this->child_sa, | |
347 | encr_r, integ_r, this->spi_i, this->cpi_i, | |
348 | this->initiator, FALSE, FALSE, tsr, tsi); | |
5351d63c MW |
349 | } |
350 | } | |
5351d63c MW |
351 | |
352 | if (status_i != SUCCESS || status_o != SUCCESS) | |
353 | { | |
354 | DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel", | |
355 | (status_i != SUCCESS) ? "inbound " : "", | |
356 | (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "", | |
357 | (status_o != SUCCESS) ? "outbound " : ""); | |
696fa8e0 MW |
358 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); |
359 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
c4a286c8 | 360 | status = FAILED; |
5351d63c MW |
361 | } |
362 | else | |
363 | { | |
c4a286c8 TB |
364 | if (this->initiator) |
365 | { | |
366 | status = this->child_sa->add_policies(this->child_sa, tsi, tsr); | |
367 | } | |
368 | else | |
369 | { | |
370 | status = this->child_sa->add_policies(this->child_sa, tsr, tsi); | |
371 | } | |
372 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
373 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
374 | if (status != SUCCESS) | |
375 | { | |
376 | DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); | |
377 | } | |
378 | else | |
379 | { | |
380 | charon->bus->child_derived_keys(charon->bus, this->child_sa, | |
381 | this->initiator, encr_i, encr_r, | |
382 | integ_i, integ_r); | |
383 | } | |
5351d63c | 384 | } |
c4a286c8 TB |
385 | chunk_clear(&integ_i); |
386 | chunk_clear(&integ_r); | |
387 | chunk_clear(&encr_i); | |
388 | chunk_clear(&encr_r); | |
389 | ||
5351d63c MW |
390 | if (status != SUCCESS) |
391 | { | |
5351d63c MW |
392 | return FALSE; |
393 | } | |
394 | ||
395 | charon->bus->child_keys(charon->bus, this->child_sa, this->initiator, | |
5adf855e | 396 | this->dh, this->nonce_i, this->nonce_r); |
5351d63c MW |
397 | |
398 | /* add to IKE_SA, and remove from task */ | |
399 | this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); | |
400 | this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); | |
401 | ||
553bb787 MW |
402 | my_ts = linked_list_create_from_enumerator( |
403 | this->child_sa->create_ts_enumerator(this->child_sa, TRUE)); | |
404 | other_ts = linked_list_create_from_enumerator( | |
405 | this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); | |
406 | ||
5351d63c | 407 | DBG0(DBG_IKE, "CHILD_SA %s{%d} established " |
ebeb8c87 | 408 | "with SPIs %.8x_i %.8x_o and TS %#R === %#R", |
5351d63c | 409 | this->child_sa->get_name(this->child_sa), |
246c969d | 410 | this->child_sa->get_unique_id(this->child_sa), |
5351d63c | 411 | ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), |
553bb787 MW |
412 | ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts); |
413 | ||
414 | my_ts->destroy(my_ts); | |
415 | other_ts->destroy(other_ts); | |
5351d63c | 416 | |
669d8bde MW |
417 | if (this->rekey) |
418 | { | |
419 | old = this->ike_sa->get_child_sa(this->ike_sa, | |
420 | this->proposal->get_protocol(this->proposal), | |
421 | this->rekey, TRUE); | |
422 | } | |
423 | if (old) | |
424 | { | |
425 | charon->bus->child_rekey(charon->bus, old, this->child_sa); | |
2f3c08d2 TB |
426 | /* rekeyed CHILD_SAs stay installed until they expire or are deleted |
427 | * by the other peer */ | |
70728eb1 | 428 | old->set_state(old, CHILD_REKEYED); |
2f3c08d2 TB |
429 | /* as initiator we delete the CHILD_SA if configured to do so */ |
430 | if (this->initiator && this->delete) | |
431 | { | |
432 | this->ike_sa->queue_task(this->ike_sa, | |
433 | (task_t*)quick_delete_create(this->ike_sa, | |
434 | this->proposal->get_protocol(this->proposal), | |
435 | this->rekey, TRUE, FALSE)); | |
436 | } | |
669d8bde MW |
437 | } |
438 | else | |
439 | { | |
440 | charon->bus->child_updown(charon->bus, this->child_sa, TRUE); | |
441 | } | |
85ace154 | 442 | schedule_inactivity_timeout(this); |
5351d63c | 443 | this->child_sa = NULL; |
5351d63c MW |
444 | return TRUE; |
445 | } | |
446 | ||
818330aa MW |
447 | /** |
448 | * Generate and add NONCE | |
449 | */ | |
450 | static bool add_nonce(private_quick_mode_t *this, chunk_t *nonce, | |
451 | message_t *message) | |
452 | { | |
453 | nonce_payload_t *nonce_payload; | |
afaf1bdf | 454 | nonce_gen_t *nonceg; |
818330aa | 455 | |
afaf1bdf AKR |
456 | nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat); |
457 | if (!nonceg) | |
818330aa | 458 | { |
afaf1bdf | 459 | DBG1(DBG_IKE, "no nonce generator found to create nonce"); |
818330aa MW |
460 | return FALSE; |
461 | } | |
605985d1 RB |
462 | if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, nonce)) |
463 | { | |
464 | DBG1(DBG_IKE, "nonce allocation failed"); | |
465 | nonceg->destroy(nonceg); | |
466 | return FALSE; | |
467 | } | |
afaf1bdf | 468 | nonceg->destroy(nonceg); |
818330aa | 469 | |
3ecfc83c | 470 | nonce_payload = nonce_payload_create(PLV1_NONCE); |
818330aa MW |
471 | nonce_payload->set_nonce(nonce_payload, *nonce); |
472 | message->add_payload(message, &nonce_payload->payload_interface); | |
473 | ||
474 | return TRUE; | |
475 | } | |
476 | ||
477 | /** | |
478 | * Extract nonce from NONCE payload | |
479 | */ | |
480 | static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce, | |
481 | message_t *message) | |
482 | { | |
483 | nonce_payload_t *nonce_payload; | |
484 | ||
3ecfc83c | 485 | nonce_payload = (nonce_payload_t*)message->get_payload(message, PLV1_NONCE); |
818330aa MW |
486 | if (!nonce_payload) |
487 | { | |
488 | DBG1(DBG_IKE, "NONCE payload missing in message"); | |
489 | return FALSE; | |
490 | } | |
491 | *nonce = nonce_payload->get_nonce(nonce_payload); | |
492 | ||
493 | return TRUE; | |
494 | } | |
495 | ||
5adf855e MW |
496 | /** |
497 | * Add KE payload to message | |
498 | */ | |
520d58e0 | 499 | static bool add_ke(private_quick_mode_t *this, message_t *message) |
5adf855e MW |
500 | { |
501 | ke_payload_t *ke_payload; | |
502 | ||
520d58e0 MW |
503 | ke_payload = ke_payload_create_from_diffie_hellman(PLV1_KEY_EXCHANGE, |
504 | this->dh); | |
505 | if (!ke_payload) | |
506 | { | |
507 | DBG1(DBG_IKE, "creating KE payload failed"); | |
508 | return FALSE; | |
509 | } | |
5adf855e | 510 | message->add_payload(message, &ke_payload->payload_interface); |
520d58e0 | 511 | return TRUE; |
5adf855e MW |
512 | } |
513 | ||
514 | /** | |
515 | * Get DH value from a KE payload | |
516 | */ | |
517 | static bool get_ke(private_quick_mode_t *this, message_t *message) | |
518 | { | |
519 | ke_payload_t *ke_payload; | |
520 | ||
3ecfc83c | 521 | ke_payload = (ke_payload_t*)message->get_payload(message, PLV1_KEY_EXCHANGE); |
5adf855e MW |
522 | if (!ke_payload) |
523 | { | |
524 | DBG1(DBG_IKE, "KE payload missing"); | |
525 | return FALSE; | |
526 | } | |
66147ef6 | 527 | if (!this->dh->set_other_public_value(this->dh, |
a777155f MW |
528 | ke_payload->get_key_exchange_data(ke_payload))) |
529 | { | |
530 | DBG1(DBG_IKE, "unable to apply received KE value"); | |
531 | return FALSE; | |
532 | } | |
5adf855e MW |
533 | return TRUE; |
534 | } | |
535 | ||
c4b8539f MW |
536 | /** |
537 | * Select a traffic selector from configuration | |
538 | */ | |
36431795 MW |
539 | static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, |
540 | linked_list_t *supplied) | |
c4b8539f MW |
541 | { |
542 | traffic_selector_t *ts; | |
7ee37114 | 543 | linked_list_t *list, *hosts; |
c4b8539f | 544 | |
7ee37114 MW |
545 | hosts = get_dynamic_hosts(this->ike_sa, local); |
546 | list = this->config->get_traffic_selectors(this->config, | |
547 | local, supplied, hosts); | |
548 | hosts->destroy(hosts); | |
c4b8539f MW |
549 | if (list->get_first(list, (void**)&ts) == SUCCESS) |
550 | { | |
c4b8539f MW |
551 | ts = ts->clone(ts); |
552 | } | |
553 | else | |
554 | { | |
555 | DBG1(DBG_IKE, "%s traffic selector missing in configuration", | |
cdc42256 | 556 | local ? "local" : "remote"); |
c4b8539f MW |
557 | ts = NULL; |
558 | } | |
559 | list->destroy_offset(list, offsetof(traffic_selector_t, destroy)); | |
560 | return ts; | |
561 | } | |
562 | ||
563 | /** | |
564 | * Add selected traffic selectors to message | |
565 | */ | |
9cc38c8e | 566 | static void add_ts(private_quick_mode_t *this, message_t *message) |
c4b8539f MW |
567 | { |
568 | id_payload_t *id_payload; | |
569 | ||
77ccff82 TB |
570 | id_payload = id_payload_create_from_ts(this->tsi); |
571 | message->add_payload(message, &id_payload->payload_interface); | |
572 | id_payload = id_payload_create_from_ts(this->tsr); | |
573 | message->add_payload(message, &id_payload->payload_interface); | |
c4b8539f MW |
574 | } |
575 | ||
576 | /** | |
577 | * Get traffic selectors from received message | |
578 | */ | |
9cc38c8e | 579 | static bool get_ts(private_quick_mode_t *this, message_t *message) |
c4b8539f MW |
580 | { |
581 | traffic_selector_t *tsi = NULL, *tsr = NULL; | |
582 | enumerator_t *enumerator; | |
583 | id_payload_t *id_payload; | |
584 | payload_t *payload; | |
585 | host_t *hsi, *hsr; | |
586 | bool first = TRUE; | |
587 | ||
588 | enumerator = message->create_payload_enumerator(message); | |
589 | while (enumerator->enumerate(enumerator, &payload)) | |
590 | { | |
3ecfc83c | 591 | if (payload->get_type(payload) == PLV1_ID) |
c4b8539f MW |
592 | { |
593 | id_payload = (id_payload_t*)payload; | |
594 | ||
595 | if (first) | |
596 | { | |
597 | tsi = id_payload->get_ts(id_payload); | |
598 | first = FALSE; | |
599 | } | |
600 | else | |
601 | { | |
602 | tsr = id_payload->get_ts(id_payload); | |
603 | break; | |
604 | } | |
605 | } | |
606 | } | |
607 | enumerator->destroy(enumerator); | |
608 | ||
609 | /* create host2host selectors if ID payloads missing */ | |
9cc38c8e | 610 | if (this->initiator) |
c4b8539f MW |
611 | { |
612 | hsi = this->ike_sa->get_my_host(this->ike_sa); | |
613 | hsr = this->ike_sa->get_other_host(this->ike_sa); | |
614 | } | |
615 | else | |
616 | { | |
617 | hsr = this->ike_sa->get_my_host(this->ike_sa); | |
618 | hsi = this->ike_sa->get_other_host(this->ike_sa); | |
619 | } | |
620 | if (!tsi) | |
621 | { | |
622 | tsi = traffic_selector_create_from_subnet(hsi->clone(hsi), | |
a1db77de | 623 | hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0, 65535); |
c4b8539f MW |
624 | } |
625 | if (!tsr) | |
626 | { | |
627 | tsr = traffic_selector_create_from_subnet(hsr->clone(hsr), | |
a1db77de | 628 | hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0, 65535); |
c4b8539f | 629 | } |
283898d6 | 630 | if (this->mode == MODE_TRANSPORT && this->udp && |
a889cfe5 TB |
631 | (!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr))) |
632 | { /* change TS in case of a NAT in transport mode */ | |
633 | DBG2(DBG_IKE, "changing received traffic selectors %R=== %R due to NAT", | |
634 | tsi, tsr); | |
635 | tsi->set_address(tsi, hsi); | |
636 | tsr->set_address(tsr, hsr); | |
637 | } | |
638 | ||
9cc38c8e | 639 | if (this->initiator) |
c4b8539f | 640 | { |
9d9042d6 MW |
641 | traffic_selector_t *tsisub, *tsrsub; |
642 | ||
29906717 | 643 | /* check if peer selection is valid */ |
9d9042d6 MW |
644 | tsisub = this->tsi->get_subset(this->tsi, tsi); |
645 | tsrsub = this->tsr->get_subset(this->tsr, tsr); | |
646 | if (!tsisub || !tsrsub) | |
c4b8539f | 647 | { |
29906717 | 648 | DBG1(DBG_IKE, "peer selected invalid traffic selectors: " |
c4b8539f | 649 | "%R for %R, %R for %R", tsi, this->tsi, tsr, this->tsr); |
9d9042d6 MW |
650 | DESTROY_IF(tsisub); |
651 | DESTROY_IF(tsrsub); | |
c4b8539f MW |
652 | tsi->destroy(tsi); |
653 | tsr->destroy(tsr); | |
654 | return FALSE; | |
655 | } | |
9d9042d6 MW |
656 | tsi->destroy(tsi); |
657 | tsr->destroy(tsr); | |
c4b8539f MW |
658 | this->tsi->destroy(this->tsi); |
659 | this->tsr->destroy(this->tsr); | |
9d9042d6 MW |
660 | this->tsi = tsisub; |
661 | this->tsr = tsrsub; | |
c4b8539f MW |
662 | } |
663 | else | |
664 | { | |
665 | this->tsi = tsi; | |
666 | this->tsr = tsr; | |
667 | } | |
668 | return TRUE; | |
669 | } | |
670 | ||
0ff8d20a VR |
671 | /** |
672 | * Get encap | |
673 | */ | |
674 | static encap_t get_encap(ike_sa_t* ike_sa, bool udp) | |
675 | { | |
676 | if (!udp) | |
677 | { | |
678 | return ENCAP_NONE; | |
679 | } | |
680 | if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03)) | |
681 | { | |
682 | return ENCAP_UDP_DRAFT_00_03; | |
683 | } | |
684 | return ENCAP_UDP; | |
685 | } | |
686 | ||
687 | /** | |
688 | * Get NAT-OA payload type (RFC 3947 or RFC 3947 drafts). | |
689 | */ | |
690 | static payload_type_t get_nat_oa_payload_type(ike_sa_t *ike_sa) | |
691 | { | |
692 | if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03)) | |
693 | { | |
3ecfc83c | 694 | return PLV1_NAT_OA_DRAFT_00_03; |
0ff8d20a | 695 | } |
3ecfc83c | 696 | return PLV1_NAT_OA; |
0ff8d20a VR |
697 | } |
698 | ||
3bf0be6b TB |
699 | /** |
700 | * Add NAT-OA payloads | |
701 | */ | |
702 | static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message) | |
703 | { | |
704 | identification_t *id; | |
705 | id_payload_t *nat_oa; | |
706 | host_t *src, *dst; | |
0ff8d20a | 707 | payload_type_t nat_oa_payload_type; |
3bf0be6b TB |
708 | |
709 | src = message->get_source(message); | |
710 | dst = message->get_destination(message); | |
711 | ||
712 | src = this->initiator ? src : dst; | |
713 | dst = this->initiator ? dst : src; | |
714 | ||
0ff8d20a VR |
715 | nat_oa_payload_type = get_nat_oa_payload_type(this->ike_sa); |
716 | ||
3bf0be6b TB |
717 | /* first NAT-OA is the initiator's address */ |
718 | id = identification_create_from_sockaddr(src->get_sockaddr(src)); | |
0ff8d20a | 719 | nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); |
3bf0be6b TB |
720 | message->add_payload(message, (payload_t*)nat_oa); |
721 | id->destroy(id); | |
722 | ||
723 | /* second NAT-OA is that of the responder */ | |
724 | id = identification_create_from_sockaddr(dst->get_sockaddr(dst)); | |
0ff8d20a | 725 | nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); |
3bf0be6b TB |
726 | message->add_payload(message, (payload_t*)nat_oa); |
727 | id->destroy(id); | |
728 | } | |
729 | ||
cd0017d4 MW |
730 | /** |
731 | * Look up lifetimes | |
732 | */ | |
733 | static void get_lifetimes(private_quick_mode_t *this) | |
734 | { | |
735 | lifetime_cfg_t *lft; | |
736 | ||
b1df6312 | 737 | lft = this->config->get_lifetime(this->config, TRUE); |
cd0017d4 MW |
738 | if (lft->time.life) |
739 | { | |
740 | this->lifetime = lft->time.life; | |
741 | } | |
9e01d7ca | 742 | if (lft->bytes.life) |
cd0017d4 MW |
743 | { |
744 | this->lifebytes = lft->bytes.life; | |
745 | } | |
746 | free(lft); | |
747 | } | |
748 | ||
749 | /** | |
750 | * Check and apply lifetimes | |
751 | */ | |
752 | static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload) | |
753 | { | |
b12c53ce AS |
754 | uint32_t lifetime; |
755 | uint64_t lifebytes; | |
cd0017d4 MW |
756 | |
757 | lifetime = sa_payload->get_lifetime(sa_payload); | |
758 | lifebytes = sa_payload->get_lifebytes(sa_payload); | |
759 | if (this->lifetime != lifetime) | |
760 | { | |
31bd5c8c | 761 | DBG1(DBG_IKE, "received %us lifetime, configured %us", |
cd0017d4 | 762 | lifetime, this->lifetime); |
31bd5c8c | 763 | this->lifetime = lifetime; |
cd0017d4 MW |
764 | } |
765 | if (this->lifebytes != lifebytes) | |
766 | { | |
31bd5c8c | 767 | DBG1(DBG_IKE, "received %llu lifebytes, configured %llu", |
cd0017d4 | 768 | lifebytes, this->lifebytes); |
31bd5c8c | 769 | this->lifebytes = lifebytes; |
cd0017d4 MW |
770 | } |
771 | } | |
772 | ||
e7ae90c1 MW |
773 | /** |
774 | * Set the task ready to build notify error message | |
775 | */ | |
776 | static status_t send_notify(private_quick_mode_t *this, notify_type_t type) | |
5c6abd28 CO |
777 | { |
778 | notify_payload_t *notify; | |
779 | ||
3ecfc83c | 780 | notify = notify_payload_create_from_protocol_and_type(PLV1_NOTIFY, |
908fe163 | 781 | this->proto, type); |
5c6abd28 CO |
782 | notify->set_spi(notify, this->spi_i); |
783 | ||
e7ae90c1 | 784 | this->ike_sa->queue_task(this->ike_sa, |
2ddd45c9 | 785 | (task_t*)informational_create(this->ike_sa, notify)); |
e7ae90c1 | 786 | /* cancel all active/passive tasks in favour of informational */ |
7ce504e1 MW |
787 | this->ike_sa->flush_queue(this->ike_sa, |
788 | this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE); | |
e7ae90c1 | 789 | return ALREADY_DONE; |
5c6abd28 CO |
790 | } |
791 | ||
7d938be9 TE |
792 | /** |
793 | * Prepare a list of proposals from child_config containing only the specified | |
794 | * DH group, unless it is set to MODP_NONE. | |
795 | */ | |
796 | static linked_list_t *get_proposals(private_quick_mode_t *this, | |
797 | diffie_hellman_group_t group) | |
798 | { | |
799 | linked_list_t *list; | |
800 | proposal_t *proposal; | |
801 | enumerator_t *enumerator; | |
802 | ||
803 | list = this->config->get_proposals(this->config, FALSE); | |
804 | enumerator = list->create_enumerator(list); | |
805 | while (enumerator->enumerate(enumerator, &proposal)) | |
806 | { | |
807 | if (group != MODP_NONE) | |
808 | { | |
809 | if (!proposal->has_dh_group(proposal, group)) | |
810 | { | |
811 | list->remove_at(list, enumerator); | |
812 | proposal->destroy(proposal); | |
813 | continue; | |
814 | } | |
815 | proposal->strip_dh(proposal, group); | |
816 | } | |
817 | proposal->set_spi(proposal, this->spi_i); | |
818 | } | |
819 | enumerator->destroy(enumerator); | |
820 | ||
821 | return list; | |
822 | } | |
823 | ||
2b04aa46 MW |
824 | METHOD(task_t, build_i, status_t, |
825 | private_quick_mode_t *this, message_t *message) | |
826 | { | |
21b7db99 MW |
827 | switch (this->state) |
828 | { | |
829 | case QM_INIT: | |
830 | { | |
831 | sa_payload_t *sa_payload; | |
696fa8e0 | 832 | linked_list_t *list, *tsi, *tsr; |
908fe163 | 833 | proposal_t *proposal; |
5adf855e | 834 | diffie_hellman_group_t group; |
0ff8d20a | 835 | encap_t encap; |
21b7db99 | 836 | |
7fd7ffc6 | 837 | this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); |
47b448b8 | 838 | this->mode = this->config->get_mode(this->config); |
5351d63c MW |
839 | this->child_sa = child_sa_create( |
840 | this->ike_sa->get_my_host(this->ike_sa), | |
841 | this->ike_sa->get_other_host(this->ike_sa), | |
85b23888 MW |
842 | this->config, this->reqid, this->udp, |
843 | this->mark_in, this->mark_out); | |
5351d63c | 844 | |
00e11bce TB |
845 | if (this->udp && this->mode == MODE_TRANSPORT) |
846 | { | |
847 | /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */ | |
848 | add_nat_oa_payloads(this, message); | |
849 | } | |
5351d63c | 850 | |
47b448b8 TB |
851 | if (this->config->use_ipcomp(this->config)) |
852 | { | |
44d9970f MW |
853 | this->cpi_i = this->child_sa->alloc_cpi(this->child_sa); |
854 | if (!this->cpi_i) | |
47b448b8 | 855 | { |
44d9970f MW |
856 | DBG1(DBG_IKE, "unable to allocate a CPI from kernel, " |
857 | "IPComp disabled"); | |
47b448b8 TB |
858 | } |
859 | } | |
860 | ||
908fe163 MW |
861 | list = this->config->get_proposals(this->config, MODP_NONE); |
862 | if (list->get_first(list, (void**)&proposal) == SUCCESS) | |
863 | { | |
864 | this->proto = proposal->get_protocol(proposal); | |
865 | } | |
866 | list->destroy_offset(list, offsetof(proposal_t, destroy)); | |
867 | this->spi_i = this->child_sa->alloc_spi(this->child_sa, this->proto); | |
5351d63c MW |
868 | if (!this->spi_i) |
869 | { | |
870 | DBG1(DBG_IKE, "allocating SPI from kernel failed"); | |
871 | return FAILED; | |
872 | } | |
908fe163 | 873 | |
f48e7272 TB |
874 | group = this->config->get_dh_group(this->config); |
875 | if (group != MODP_NONE) | |
876 | { | |
7d938be9 | 877 | proposal_t *proposal; |
b12c53ce | 878 | uint16_t preferred_group; |
7d938be9 TE |
879 | |
880 | proposal = this->ike_sa->get_proposal(this->ike_sa); | |
881 | proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, | |
882 | &preferred_group, NULL); | |
883 | /* try the negotiated DH group from IKE_SA */ | |
884 | list = get_proposals(this, preferred_group); | |
885 | if (list->get_count(list)) | |
886 | { | |
887 | group = preferred_group; | |
888 | } | |
889 | else | |
890 | { | |
891 | /* fall back to the first configured DH group */ | |
892 | list->destroy(list); | |
893 | list = get_proposals(this, group); | |
894 | } | |
895 | ||
f48e7272 TB |
896 | this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, |
897 | group); | |
898 | if (!this->dh) | |
899 | { | |
900 | DBG1(DBG_IKE, "configured DH group %N not supported", | |
901 | diffie_hellman_group_names, group); | |
7d938be9 | 902 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
f48e7272 TB |
903 | return FAILED; |
904 | } | |
905 | } | |
7d938be9 | 906 | else |
5351d63c | 907 | { |
7d938be9 | 908 | list = get_proposals(this, MODP_NONE); |
5351d63c | 909 | } |
5351d63c | 910 | |
cd0017d4 | 911 | get_lifetimes(this); |
0ff8d20a | 912 | encap = get_encap(this->ike_sa, this->udp); |
e174e0d4 | 913 | sa_payload = sa_payload_create_from_proposals_v1(list, |
cd0017d4 | 914 | this->lifetime, this->lifebytes, AUTH_NONE, |
0ff8d20a | 915 | this->mode, encap, this->cpi_i); |
21b7db99 MW |
916 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
917 | message->add_payload(message, &sa_payload->payload_interface); | |
918 | ||
818330aa | 919 | if (!add_nonce(this, &this->nonce_i, message)) |
21b7db99 | 920 | { |
21b7db99 MW |
921 | return FAILED; |
922 | } | |
5adf855e MW |
923 | if (group != MODP_NONE) |
924 | { | |
520d58e0 MW |
925 | if (!add_ke(this, message)) |
926 | { | |
927 | return FAILED; | |
928 | } | |
5adf855e | 929 | } |
d61f2906 MW |
930 | if (!this->tsi) |
931 | { | |
932 | this->tsi = select_ts(this, TRUE, NULL); | |
933 | } | |
934 | if (!this->tsr) | |
935 | { | |
936 | this->tsr = select_ts(this, FALSE, NULL); | |
937 | } | |
abdb82fc MW |
938 | tsi = linked_list_create_with_items(this->tsi, NULL); |
939 | tsr = linked_list_create_with_items(this->tsr, NULL); | |
696fa8e0 MW |
940 | this->tsi = this->tsr = NULL; |
941 | charon->bus->narrow(charon->bus, this->child_sa, | |
942 | NARROW_INITIATOR_PRE_AUTH, tsi, tsr); | |
943 | tsi->remove_first(tsi, (void**)&this->tsi); | |
944 | tsr->remove_first(tsr, (void**)&this->tsr); | |
945 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
946 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
c4b8539f | 947 | if (!this->tsi || !this->tsr) |
21b7db99 | 948 | { |
21b7db99 MW |
949 | return FAILED; |
950 | } | |
9cc38c8e | 951 | add_ts(this, message); |
21b7db99 MW |
952 | return NEED_MORE; |
953 | } | |
954 | case QM_NEGOTIATED: | |
955 | { | |
21b7db99 MW |
956 | return SUCCESS; |
957 | } | |
958 | default: | |
959 | return FAILED; | |
960 | } | |
2b04aa46 MW |
961 | } |
962 | ||
1755ac06 MW |
963 | /** |
964 | * Check for notify errors, return TRUE if error found | |
965 | */ | |
966 | static bool has_notify_errors(private_quick_mode_t *this, message_t *message) | |
07abb470 | 967 | { |
1755ac06 MW |
968 | enumerator_t *enumerator; |
969 | payload_t *payload; | |
970 | bool err = FALSE; | |
971 | ||
972 | enumerator = message->create_payload_enumerator(message); | |
973 | while (enumerator->enumerate(enumerator, &payload)) | |
07abb470 | 974 | { |
3ecfc83c | 975 | if (payload->get_type(payload) == PLV1_NOTIFY) |
1755ac06 MW |
976 | { |
977 | notify_payload_t *notify; | |
978 | notify_type_t type; | |
979 | ||
980 | notify = (notify_payload_t*)payload; | |
981 | type = notify->get_notify_type(notify); | |
982 | if (type < 16384) | |
983 | { | |
bf5b1d9e | 984 | |
1755ac06 MW |
985 | DBG1(DBG_IKE, "received %N error notify", |
986 | notify_type_names, type); | |
987 | err = TRUE; | |
988 | } | |
989 | else | |
990 | { | |
991 | DBG1(DBG_IKE, "received %N notify", notify_type_names, type); | |
992 | } | |
993 | } | |
07abb470 | 994 | } |
1755ac06 MW |
995 | enumerator->destroy(enumerator); |
996 | ||
997 | return err; | |
07abb470 CO |
998 | } |
999 | ||
f56c3c53 MW |
1000 | /** |
1001 | * Check if this is a rekey for an existing CHILD_SA, reuse reqid if so | |
1002 | */ | |
1003 | static void check_for_rekeyed_child(private_quick_mode_t *this) | |
1004 | { | |
1005 | enumerator_t *enumerator, *policies; | |
1006 | traffic_selector_t *local, *remote; | |
1007 | child_sa_t *child_sa; | |
bee8b5e3 MW |
1008 | proposal_t *proposal; |
1009 | char *name; | |
f56c3c53 | 1010 | |
bee8b5e3 | 1011 | name = this->config->get_name(this->config); |
f56c3c53 MW |
1012 | enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); |
1013 | while (this->reqid == 0 && enumerator->enumerate(enumerator, &child_sa)) | |
1014 | { | |
bee8b5e3 | 1015 | if (streq(child_sa->get_name(child_sa), name)) |
f56c3c53 | 1016 | { |
bee8b5e3 MW |
1017 | proposal = child_sa->get_proposal(child_sa); |
1018 | switch (child_sa->get_state(child_sa)) | |
f56c3c53 | 1019 | { |
bee8b5e3 MW |
1020 | case CHILD_INSTALLED: |
1021 | case CHILD_REKEYING: | |
1022 | policies = child_sa->create_policy_enumerator(child_sa); | |
1023 | if (policies->enumerate(policies, &local, &remote) && | |
1024 | local->equals(local, this->tsr) && | |
1025 | remote->equals(remote, this->tsi) && | |
1026 | this->proposal->equals(this->proposal, proposal)) | |
1027 | { | |
1028 | this->reqid = child_sa->get_reqid(child_sa); | |
1029 | this->rekey = child_sa->get_spi(child_sa, TRUE); | |
85b23888 MW |
1030 | this->mark_in = child_sa->get_mark(child_sa, |
1031 | TRUE).value; | |
1032 | this->mark_out = child_sa->get_mark(child_sa, | |
1033 | FALSE).value; | |
bee8b5e3 MW |
1034 | child_sa->set_state(child_sa, CHILD_REKEYING); |
1035 | DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}", | |
246c969d MW |
1036 | child_sa->get_name(child_sa), |
1037 | child_sa->get_unique_id(child_sa)); | |
bee8b5e3 MW |
1038 | } |
1039 | policies->destroy(policies); | |
bf3bed1c TB |
1040 | break; |
1041 | case CHILD_REKEYED: | |
1042 | default: | |
1043 | break; | |
f56c3c53 | 1044 | } |
f56c3c53 MW |
1045 | } |
1046 | } | |
1047 | enumerator->destroy(enumerator); | |
1048 | } | |
1049 | ||
2b04aa46 MW |
1050 | METHOD(task_t, process_r, status_t, |
1051 | private_quick_mode_t *this, message_t *message) | |
1052 | { | |
4de361d9 TB |
1053 | if (this->mid && this->mid != message->get_message_id(message)) |
1054 | { /* not responsible for this quick mode exchange */ | |
37a22a16 | 1055 | return INVALID_ARG; |
4de361d9 TB |
1056 | } |
1057 | ||
21b7db99 MW |
1058 | switch (this->state) |
1059 | { | |
1060 | case QM_INIT: | |
1061 | { | |
1062 | sa_payload_t *sa_payload; | |
7ee37114 | 1063 | linked_list_t *tsi, *tsr, *hostsi, *hostsr, *list = NULL; |
21b7db99 | 1064 | peer_cfg_t *peer_cfg; |
b12c53ce | 1065 | uint16_t group; |
0a954d67 | 1066 | bool private, prefer_configured; |
21b7db99 | 1067 | |
a889cfe5 | 1068 | sa_payload = (sa_payload_t*)message->get_payload(message, |
3ecfc83c | 1069 | PLV1_SECURITY_ASSOCIATION); |
a889cfe5 TB |
1070 | if (!sa_payload) |
1071 | { | |
1072 | DBG1(DBG_IKE, "sa payload missing"); | |
1073 | return send_notify(this, INVALID_PAYLOAD_TYPE); | |
1074 | } | |
1075 | ||
1076 | this->mode = sa_payload->get_encap_mode(sa_payload, &this->udp); | |
1077 | ||
9cc38c8e | 1078 | if (!get_ts(this, message)) |
c4c59504 | 1079 | { |
c4b8539f | 1080 | return FAILED; |
c4c59504 | 1081 | } |
21b7db99 | 1082 | peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); |
abdb82fc MW |
1083 | tsi = linked_list_create_with_items(this->tsi, NULL); |
1084 | tsr = linked_list_create_with_items(this->tsr, NULL); | |
36431795 | 1085 | this->tsi = this->tsr = NULL; |
7ee37114 MW |
1086 | hostsi = get_dynamic_hosts(this->ike_sa, FALSE); |
1087 | hostsr = get_dynamic_hosts(this->ike_sa, TRUE); | |
21b7db99 | 1088 | this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi, |
7ee37114 MW |
1089 | hostsr, hostsi); |
1090 | hostsi->destroy(hostsi); | |
1091 | hostsr->destroy(hostsr); | |
28e3c659 MW |
1092 | if (this->config) |
1093 | { | |
1094 | this->tsi = select_ts(this, FALSE, tsi); | |
1095 | this->tsr = select_ts(this, TRUE, tsr); | |
1096 | } | |
36431795 MW |
1097 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); |
1098 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
2d38a03d TB |
1099 | if (!this->config || !this->tsi || !this->tsr || |
1100 | this->mode != this->config->get_mode(this->config)) | |
21b7db99 | 1101 | { |
5f1df0a0 MW |
1102 | DBG1(DBG_IKE, "no matching CHILD_SA config found"); |
1103 | return send_notify(this, INVALID_ID_INFORMATION); | |
21b7db99 MW |
1104 | } |
1105 | ||
47b448b8 TB |
1106 | if (this->config->use_ipcomp(this->config)) |
1107 | { | |
44d9970f MW |
1108 | list = sa_payload->get_ipcomp_proposals(sa_payload, |
1109 | &this->cpi_i); | |
1110 | if (!list->get_count(list)) | |
47b448b8 | 1111 | { |
44d9970f MW |
1112 | DBG1(DBG_IKE, "expected IPComp proposal but peer did " |
1113 | "not send one, IPComp disabled"); | |
1114 | this->cpi_i = 0; | |
47b448b8 TB |
1115 | } |
1116 | } | |
1117 | if (!list || !list->get_count(list)) | |
1118 | { | |
1119 | DESTROY_IF(list); | |
47b448b8 TB |
1120 | list = sa_payload->get_proposals(sa_payload); |
1121 | } | |
44bd9b48 AS |
1122 | private = this->ike_sa->supports_extension(this->ike_sa, |
1123 | EXT_STRONGSWAN); | |
0a954d67 TB |
1124 | prefer_configured = lib->settings->get_bool(lib->settings, |
1125 | "%s.prefer_configured_proposals", TRUE, lib->ns); | |
f2ea230b | 1126 | this->proposal = this->config->select_proposal(this->config, list, |
0a954d67 | 1127 | FALSE, private, prefer_configured); |
21b7db99 | 1128 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
cd0017d4 MW |
1129 | |
1130 | get_lifetimes(this); | |
1131 | apply_lifetimes(this, sa_payload); | |
1132 | ||
21b7db99 MW |
1133 | if (!this->proposal) |
1134 | { | |
e7ae90c1 MW |
1135 | DBG1(DBG_IKE, "no matching proposal found, sending %N", |
1136 | notify_type_names, NO_PROPOSAL_CHOSEN); | |
1137 | return send_notify(this, NO_PROPOSAL_CHOSEN); | |
21b7db99 | 1138 | } |
5351d63c | 1139 | this->spi_i = this->proposal->get_spi(this->proposal); |
21b7db99 | 1140 | |
818330aa | 1141 | if (!get_nonce(this, &this->nonce_i, message)) |
21b7db99 | 1142 | { |
bf5b1d9e | 1143 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
21b7db99 | 1144 | } |
cd0017d4 | 1145 | |
5adf855e MW |
1146 | if (this->proposal->get_algorithm(this->proposal, |
1147 | DIFFIE_HELLMAN_GROUP, &group, NULL)) | |
1148 | { | |
1149 | this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, | |
1150 | group); | |
1151 | if (!this->dh) | |
1152 | { | |
1153 | DBG1(DBG_IKE, "negotiated DH group %N not supported", | |
1154 | diffie_hellman_group_names, group); | |
bf5b1d9e | 1155 | return send_notify(this, INVALID_KEY_INFORMATION); |
5adf855e MW |
1156 | } |
1157 | if (!get_ke(this, message)) | |
1158 | { | |
bf5b1d9e | 1159 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
5adf855e MW |
1160 | } |
1161 | } | |
cd0017d4 | 1162 | |
f56c3c53 MW |
1163 | check_for_rekeyed_child(this); |
1164 | ||
5351d63c MW |
1165 | this->child_sa = child_sa_create( |
1166 | this->ike_sa->get_my_host(this->ike_sa), | |
1167 | this->ike_sa->get_other_host(this->ike_sa), | |
85b23888 MW |
1168 | this->config, this->reqid, this->udp, |
1169 | this->mark_in, this->mark_out); | |
f942588f | 1170 | |
abdb82fc MW |
1171 | tsi = linked_list_create_with_items(this->tsi, NULL); |
1172 | tsr = linked_list_create_with_items(this->tsr, NULL); | |
f942588f MW |
1173 | this->tsi = this->tsr = NULL; |
1174 | charon->bus->narrow(charon->bus, this->child_sa, | |
1175 | NARROW_RESPONDER, tsr, tsi); | |
1176 | if (tsi->remove_first(tsi, (void**)&this->tsi) != SUCCESS || | |
1177 | tsr->remove_first(tsr, (void**)&this->tsr) != SUCCESS) | |
1178 | { | |
1179 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
1180 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
1181 | return send_notify(this, INVALID_ID_INFORMATION); | |
1182 | } | |
1183 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
1184 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
1185 | ||
21b7db99 MW |
1186 | return NEED_MORE; |
1187 | } | |
1188 | case QM_NEGOTIATED: | |
1189 | { | |
cd9bba50 | 1190 | if (has_notify_errors(this, message)) |
07abb470 | 1191 | { |
bf5b1d9e | 1192 | return SUCCESS; |
07abb470 | 1193 | } |
cd9bba50 MW |
1194 | if (message->get_exchange_type(message) == INFORMATIONAL_V1) |
1195 | { | |
1196 | if (message->get_payload(message, PLV1_DELETE)) | |
1197 | { | |
1198 | /* If the DELETE for a Quick Mode follows immediately | |
1199 | * after rekeying, we might receive it before the | |
1200 | * third completing Quick Mode message. Ignore it, as | |
1201 | * it gets handled by a separately queued delete task. */ | |
1202 | return NEED_MORE; | |
1203 | } | |
1204 | return SUCCESS; | |
1205 | } | |
5351d63c MW |
1206 | if (!install(this)) |
1207 | { | |
e91157a4 TB |
1208 | ike_sa_t *ike_sa = this->ike_sa; |
1209 | task_t *task; | |
1210 | ||
1211 | task = (task_t*)quick_delete_create(this->ike_sa, | |
767966e7 | 1212 | this->proposal->get_protocol(this->proposal), |
e91157a4 TB |
1213 | this->spi_i, TRUE, TRUE); |
1214 | /* flush_queue() destroys the current task */ | |
1215 | ike_sa->flush_queue(ike_sa, TASK_QUEUE_PASSIVE); | |
1216 | ike_sa->queue_task(ike_sa, task); | |
767966e7 | 1217 | return ALREADY_DONE; |
5351d63c | 1218 | } |
21b7db99 MW |
1219 | return SUCCESS; |
1220 | } | |
1221 | default: | |
1222 | return FAILED; | |
1223 | } | |
2b04aa46 MW |
1224 | } |
1225 | ||
1226 | METHOD(task_t, build_r, status_t, | |
1227 | private_quick_mode_t *this, message_t *message) | |
1228 | { | |
4de361d9 TB |
1229 | if (this->mid && this->mid != message->get_message_id(message)) |
1230 | { /* not responsible for this quick mode exchange */ | |
37a22a16 | 1231 | return INVALID_ARG; |
4de361d9 TB |
1232 | } |
1233 | ||
21b7db99 MW |
1234 | switch (this->state) |
1235 | { | |
1236 | case QM_INIT: | |
1237 | { | |
1238 | sa_payload_t *sa_payload; | |
0ff8d20a | 1239 | encap_t encap; |
21b7db99 | 1240 | |
908fe163 MW |
1241 | this->proto = this->proposal->get_protocol(this->proposal); |
1242 | this->spi_r = this->child_sa->alloc_spi(this->child_sa, this->proto); | |
5351d63c MW |
1243 | if (!this->spi_r) |
1244 | { | |
1245 | DBG1(DBG_IKE, "allocating SPI from kernel failed"); | |
bf5b1d9e | 1246 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
5351d63c MW |
1247 | } |
1248 | this->proposal->set_spi(this->proposal, this->spi_r); | |
1249 | ||
47b448b8 TB |
1250 | if (this->cpi_i) |
1251 | { | |
1252 | this->cpi_r = this->child_sa->alloc_cpi(this->child_sa); | |
1253 | if (!this->cpi_r) | |
1254 | { | |
1255 | DBG1(DBG_IKE, "unable to allocate a CPI from " | |
1256 | "kernel, IPComp disabled"); | |
1257 | return send_notify(this, NO_PROPOSAL_CHOSEN); | |
1258 | } | |
1259 | } | |
1260 | ||
7fd7ffc6 | 1261 | if (this->udp && this->mode == MODE_TRANSPORT) |
29b0cb32 | 1262 | { |
3bf0be6b | 1263 | /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */ |
9f80110b | 1264 | add_nat_oa_payloads(this, message); |
29b0cb32 TB |
1265 | } |
1266 | ||
0ff8d20a | 1267 | encap = get_encap(this->ike_sa, this->udp); |
e174e0d4 | 1268 | sa_payload = sa_payload_create_from_proposal_v1(this->proposal, |
cd0017d4 | 1269 | this->lifetime, this->lifebytes, AUTH_NONE, |
0ff8d20a | 1270 | this->mode, encap, this->cpi_r); |
21b7db99 MW |
1271 | message->add_payload(message, &sa_payload->payload_interface); |
1272 | ||
818330aa | 1273 | if (!add_nonce(this, &this->nonce_r, message)) |
21b7db99 | 1274 | { |
21b7db99 MW |
1275 | return FAILED; |
1276 | } | |
5adf855e MW |
1277 | if (this->dh) |
1278 | { | |
520d58e0 MW |
1279 | if (!add_ke(this, message)) |
1280 | { | |
1281 | return FAILED; | |
1282 | } | |
5adf855e MW |
1283 | } |
1284 | ||
9cc38c8e | 1285 | add_ts(this, message); |
21b7db99 | 1286 | |
21b7db99 | 1287 | this->state = QM_NEGOTIATED; |
4de361d9 | 1288 | this->mid = message->get_message_id(message); |
21b7db99 MW |
1289 | return NEED_MORE; |
1290 | } | |
cd9bba50 MW |
1291 | case QM_NEGOTIATED: |
1292 | if (message->get_exchange_type(message) == INFORMATIONAL_V1) | |
1293 | { | |
1294 | /* skip INFORMATIONAL response if we received a INFORMATIONAL | |
1295 | * delete, see process_r() */ | |
1296 | return ALREADY_DONE; | |
1297 | } | |
1298 | /* fall */ | |
21b7db99 MW |
1299 | default: |
1300 | return FAILED; | |
1301 | } | |
2b04aa46 MW |
1302 | } |
1303 | ||
1304 | METHOD(task_t, process_i, status_t, | |
1305 | private_quick_mode_t *this, message_t *message) | |
1306 | { | |
21b7db99 MW |
1307 | switch (this->state) |
1308 | { | |
1309 | case QM_INIT: | |
1310 | { | |
1311 | sa_payload_t *sa_payload; | |
47b448b8 | 1312 | linked_list_t *list = NULL; |
44bd9b48 | 1313 | bool private; |
21b7db99 MW |
1314 | |
1315 | sa_payload = (sa_payload_t*)message->get_payload(message, | |
3ecfc83c | 1316 | PLV1_SECURITY_ASSOCIATION); |
21b7db99 MW |
1317 | if (!sa_payload) |
1318 | { | |
1319 | DBG1(DBG_IKE, "sa payload missing"); | |
bf5b1d9e | 1320 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
21b7db99 | 1321 | } |
47b448b8 TB |
1322 | if (this->cpi_i) |
1323 | { | |
1324 | list = sa_payload->get_ipcomp_proposals(sa_payload, | |
1325 | &this->cpi_r); | |
3451ecd7 TB |
1326 | if (!list->get_count(list)) |
1327 | { | |
1328 | DBG1(DBG_IKE, "peer did not acccept our IPComp proposal, " | |
1329 | "IPComp disabled"); | |
1330 | this->cpi_i = 0; | |
1331 | } | |
47b448b8 TB |
1332 | } |
1333 | if (!list || !list->get_count(list)) | |
1334 | { | |
1335 | DESTROY_IF(list); | |
47b448b8 TB |
1336 | list = sa_payload->get_proposals(sa_payload); |
1337 | } | |
44bd9b48 AS |
1338 | private = this->ike_sa->supports_extension(this->ike_sa, |
1339 | EXT_STRONGSWAN); | |
f2ea230b TB |
1340 | this->proposal = this->config->select_proposal(this->config, list, |
1341 | FALSE, private, TRUE); | |
21b7db99 MW |
1342 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
1343 | if (!this->proposal) | |
1344 | { | |
1345 | DBG1(DBG_IKE, "no matching proposal found"); | |
bf5b1d9e | 1346 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
21b7db99 | 1347 | } |
5351d63c MW |
1348 | this->spi_r = this->proposal->get_spi(this->proposal); |
1349 | ||
cd0017d4 MW |
1350 | apply_lifetimes(this, sa_payload); |
1351 | ||
818330aa | 1352 | if (!get_nonce(this, &this->nonce_r, message)) |
21b7db99 | 1353 | { |
bf5b1d9e | 1354 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
21b7db99 | 1355 | } |
5adf855e MW |
1356 | if (this->dh && !get_ke(this, message)) |
1357 | { | |
bf5b1d9e | 1358 | return send_notify(this, INVALID_KEY_INFORMATION); |
5adf855e | 1359 | } |
9cc38c8e | 1360 | if (!get_ts(this, message)) |
c4b8539f | 1361 | { |
bf5b1d9e | 1362 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
c4b8539f | 1363 | } |
5351d63c MW |
1364 | if (!install(this)) |
1365 | { | |
bf5b1d9e | 1366 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
5351d63c | 1367 | } |
21b7db99 MW |
1368 | this->state = QM_NEGOTIATED; |
1369 | return NEED_MORE; | |
1370 | } | |
1371 | default: | |
1372 | return FAILED; | |
1373 | } | |
2b04aa46 MW |
1374 | } |
1375 | ||
1376 | METHOD(task_t, get_type, task_type_t, | |
1377 | private_quick_mode_t *this) | |
1378 | { | |
1379 | return TASK_QUICK_MODE; | |
1380 | } | |
1381 | ||
b12c53ce | 1382 | METHOD(quick_mode_t, get_mid, uint32_t, |
4de361d9 TB |
1383 | private_quick_mode_t *this) |
1384 | { | |
1385 | return this->mid; | |
1386 | } | |
1387 | ||
14dc7941 | 1388 | METHOD(quick_mode_t, use_reqid, void, |
b12c53ce | 1389 | private_quick_mode_t *this, uint32_t reqid) |
14dc7941 MW |
1390 | { |
1391 | this->reqid = reqid; | |
1392 | } | |
1393 | ||
85b23888 MW |
1394 | METHOD(quick_mode_t, use_marks, void, |
1395 | private_quick_mode_t *this, u_int in, u_int out) | |
1396 | { | |
1397 | this->mark_in = in; | |
1398 | this->mark_out = out; | |
1399 | } | |
1400 | ||
669d8bde | 1401 | METHOD(quick_mode_t, rekey, void, |
b12c53ce | 1402 | private_quick_mode_t *this, uint32_t spi) |
669d8bde MW |
1403 | { |
1404 | this->rekey = spi; | |
1405 | } | |
1406 | ||
2b04aa46 MW |
1407 | METHOD(task_t, migrate, void, |
1408 | private_quick_mode_t *this, ike_sa_t *ike_sa) | |
1409 | { | |
bce22af2 MW |
1410 | chunk_free(&this->nonce_i); |
1411 | chunk_free(&this->nonce_r); | |
1412 | DESTROY_IF(this->tsi); | |
1413 | DESTROY_IF(this->tsr); | |
1414 | DESTROY_IF(this->proposal); | |
1415 | DESTROY_IF(this->child_sa); | |
1416 | DESTROY_IF(this->dh); | |
1417 | ||
2b04aa46 | 1418 | this->ike_sa = ike_sa; |
bce22af2 MW |
1419 | this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); |
1420 | this->state = QM_INIT; | |
4de361d9 | 1421 | this->mid = 0; |
bce22af2 MW |
1422 | this->tsi = NULL; |
1423 | this->tsr = NULL; | |
1424 | this->proposal = NULL; | |
1425 | this->child_sa = NULL; | |
1426 | this->dh = NULL; | |
1427 | this->spi_i = 0; | |
1428 | this->spi_r = 0; | |
85b23888 MW |
1429 | this->mark_in = 0; |
1430 | this->mark_out = 0; | |
bce22af2 MW |
1431 | |
1432 | if (!this->initiator) | |
1433 | { | |
1434 | DESTROY_IF(this->config); | |
1435 | this->config = NULL; | |
1436 | } | |
2b04aa46 MW |
1437 | } |
1438 | ||
1439 | METHOD(task_t, destroy, void, | |
1440 | private_quick_mode_t *this) | |
1441 | { | |
1442 | chunk_free(&this->nonce_i); | |
1443 | chunk_free(&this->nonce_r); | |
1444 | DESTROY_IF(this->tsi); | |
1445 | DESTROY_IF(this->tsr); | |
1446 | DESTROY_IF(this->proposal); | |
1447 | DESTROY_IF(this->child_sa); | |
1448 | DESTROY_IF(this->config); | |
5adf855e | 1449 | DESTROY_IF(this->dh); |
2b04aa46 MW |
1450 | free(this); |
1451 | } | |
1452 | ||
1453 | /* | |
1454 | * Described in header. | |
1455 | */ | |
1456 | quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, | |
1457 | traffic_selector_t *tsi, traffic_selector_t *tsr) | |
1458 | { | |
1459 | private_quick_mode_t *this; | |
1460 | ||
1461 | INIT(this, | |
1462 | .public = { | |
1463 | .task = { | |
1464 | .get_type = _get_type, | |
1465 | .migrate = _migrate, | |
1466 | .destroy = _destroy, | |
1467 | }, | |
4de361d9 | 1468 | .get_mid = _get_mid, |
14dc7941 | 1469 | .use_reqid = _use_reqid, |
85b23888 | 1470 | .use_marks = _use_marks, |
669d8bde | 1471 | .rekey = _rekey, |
2b04aa46 MW |
1472 | }, |
1473 | .ike_sa = ike_sa, | |
5351d63c | 1474 | .initiator = config != NULL, |
2b04aa46 | 1475 | .config = config, |
5351d63c | 1476 | .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa), |
2b04aa46 | 1477 | .state = QM_INIT, |
d61f2906 MW |
1478 | .tsi = tsi ? tsi->clone(tsi) : NULL, |
1479 | .tsr = tsr ? tsr->clone(tsr) : NULL, | |
908fe163 | 1480 | .proto = PROTO_ESP, |
2f3c08d2 TB |
1481 | .delete = lib->settings->get_bool(lib->settings, |
1482 | "%s.delete_rekeyed", FALSE, lib->ns), | |
2b04aa46 MW |
1483 | ); |
1484 | ||
1485 | if (config) | |
1486 | { | |
1487 | this->public.task.build = _build_i; | |
1488 | this->public.task.process = _process_i; | |
1489 | } | |
1490 | else | |
1491 | { | |
1492 | this->public.task.build = _build_r; | |
1493 | this->public.task.process = _process_r; | |
1494 | } | |
1495 | ||
1496 | return &this->public; | |
1497 | } |