]>
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 | ||
4989aba8 TB |
328 | if (this->initiator) |
329 | { | |
330 | this->child_sa->set_policies(this->child_sa, tsi, tsr); | |
331 | } | |
332 | else | |
333 | { | |
334 | this->child_sa->set_policies(this->child_sa, tsr, tsi); | |
335 | } | |
336 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
337 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
338 | ||
5adf855e | 339 | if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh, |
a7910b1c MW |
340 | this->spi_i, this->spi_r, this->nonce_i, this->nonce_r, |
341 | &encr_i, &integ_i, &encr_r, &integ_r)) | |
5351d63c MW |
342 | { |
343 | if (this->initiator) | |
344 | { | |
a8c94544 MW |
345 | status_i = this->child_sa->install(this->child_sa, |
346 | encr_r, integ_r, this->spi_i, this->cpi_i, | |
4989aba8 | 347 | this->initiator, TRUE, FALSE); |
a8c94544 MW |
348 | status_o = this->child_sa->install(this->child_sa, |
349 | encr_i, integ_i, this->spi_r, this->cpi_r, | |
4989aba8 | 350 | this->initiator, FALSE, FALSE); |
5351d63c MW |
351 | } |
352 | else | |
353 | { | |
a8c94544 MW |
354 | status_i = this->child_sa->install(this->child_sa, |
355 | encr_i, integ_i, this->spi_r, this->cpi_r, | |
4989aba8 | 356 | this->initiator, TRUE, FALSE); |
a8c94544 MW |
357 | status_o = this->child_sa->install(this->child_sa, |
358 | encr_r, integ_r, this->spi_i, this->cpi_i, | |
4989aba8 | 359 | this->initiator, FALSE, FALSE); |
5351d63c MW |
360 | } |
361 | } | |
5351d63c MW |
362 | |
363 | if (status_i != SUCCESS || status_o != SUCCESS) | |
364 | { | |
365 | DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel", | |
366 | (status_i != SUCCESS) ? "inbound " : "", | |
367 | (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "", | |
368 | (status_o != SUCCESS) ? "outbound " : ""); | |
c4a286c8 | 369 | status = FAILED; |
5351d63c MW |
370 | } |
371 | else | |
372 | { | |
4989aba8 TB |
373 | status = this->child_sa->install_policies(this->child_sa); |
374 | ||
c4a286c8 TB |
375 | if (status != SUCCESS) |
376 | { | |
377 | DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); | |
378 | } | |
379 | else | |
380 | { | |
381 | charon->bus->child_derived_keys(charon->bus, this->child_sa, | |
382 | this->initiator, encr_i, encr_r, | |
383 | integ_i, integ_r); | |
384 | } | |
5351d63c | 385 | } |
c4a286c8 TB |
386 | chunk_clear(&integ_i); |
387 | chunk_clear(&integ_r); | |
388 | chunk_clear(&encr_i); | |
389 | chunk_clear(&encr_r); | |
390 | ||
5351d63c MW |
391 | if (status != SUCCESS) |
392 | { | |
5351d63c MW |
393 | return FALSE; |
394 | } | |
395 | ||
396 | charon->bus->child_keys(charon->bus, this->child_sa, this->initiator, | |
5adf855e | 397 | this->dh, this->nonce_i, this->nonce_r); |
5351d63c | 398 | |
553bb787 MW |
399 | my_ts = linked_list_create_from_enumerator( |
400 | this->child_sa->create_ts_enumerator(this->child_sa, TRUE)); | |
401 | other_ts = linked_list_create_from_enumerator( | |
402 | this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); | |
403 | ||
5351d63c | 404 | DBG0(DBG_IKE, "CHILD_SA %s{%d} established " |
ebeb8c87 | 405 | "with SPIs %.8x_i %.8x_o and TS %#R === %#R", |
5351d63c | 406 | this->child_sa->get_name(this->child_sa), |
246c969d | 407 | this->child_sa->get_unique_id(this->child_sa), |
5351d63c | 408 | ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), |
553bb787 MW |
409 | ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts); |
410 | ||
411 | my_ts->destroy(my_ts); | |
412 | other_ts->destroy(other_ts); | |
5351d63c | 413 | |
a0cde769 TB |
414 | this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); |
415 | this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); | |
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; | |
d8f0d9c2 | 706 | host_t *init, *resp; |
0ff8d20a | 707 | payload_type_t nat_oa_payload_type; |
3bf0be6b | 708 | |
d8f0d9c2 TB |
709 | if (this->initiator) |
710 | { | |
711 | init = message->get_source(message); | |
712 | resp = message->get_destination(message); | |
713 | } | |
714 | else | |
715 | { | |
716 | init = message->get_destination(message); | |
717 | resp = message->get_source(message); | |
718 | } | |
3bf0be6b | 719 | |
0ff8d20a VR |
720 | nat_oa_payload_type = get_nat_oa_payload_type(this->ike_sa); |
721 | ||
3bf0be6b | 722 | /* first NAT-OA is the initiator's address */ |
d8f0d9c2 | 723 | id = identification_create_from_sockaddr(init->get_sockaddr(init)); |
0ff8d20a | 724 | nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); |
3bf0be6b TB |
725 | message->add_payload(message, (payload_t*)nat_oa); |
726 | id->destroy(id); | |
727 | ||
728 | /* second NAT-OA is that of the responder */ | |
d8f0d9c2 | 729 | id = identification_create_from_sockaddr(resp->get_sockaddr(resp)); |
0ff8d20a | 730 | nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); |
3bf0be6b TB |
731 | message->add_payload(message, (payload_t*)nat_oa); |
732 | id->destroy(id); | |
733 | } | |
734 | ||
cd0017d4 MW |
735 | /** |
736 | * Look up lifetimes | |
737 | */ | |
738 | static void get_lifetimes(private_quick_mode_t *this) | |
739 | { | |
740 | lifetime_cfg_t *lft; | |
741 | ||
b1df6312 | 742 | lft = this->config->get_lifetime(this->config, TRUE); |
cd0017d4 MW |
743 | if (lft->time.life) |
744 | { | |
745 | this->lifetime = lft->time.life; | |
746 | } | |
9e01d7ca | 747 | if (lft->bytes.life) |
cd0017d4 MW |
748 | { |
749 | this->lifebytes = lft->bytes.life; | |
750 | } | |
751 | free(lft); | |
752 | } | |
753 | ||
754 | /** | |
755 | * Check and apply lifetimes | |
756 | */ | |
757 | static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload) | |
758 | { | |
b12c53ce AS |
759 | uint32_t lifetime; |
760 | uint64_t lifebytes; | |
cd0017d4 MW |
761 | |
762 | lifetime = sa_payload->get_lifetime(sa_payload); | |
763 | lifebytes = sa_payload->get_lifebytes(sa_payload); | |
764 | if (this->lifetime != lifetime) | |
765 | { | |
31bd5c8c | 766 | DBG1(DBG_IKE, "received %us lifetime, configured %us", |
cd0017d4 | 767 | lifetime, this->lifetime); |
31bd5c8c | 768 | this->lifetime = lifetime; |
cd0017d4 MW |
769 | } |
770 | if (this->lifebytes != lifebytes) | |
771 | { | |
31bd5c8c | 772 | DBG1(DBG_IKE, "received %llu lifebytes, configured %llu", |
cd0017d4 | 773 | lifebytes, this->lifebytes); |
31bd5c8c | 774 | this->lifebytes = lifebytes; |
cd0017d4 MW |
775 | } |
776 | } | |
777 | ||
e7ae90c1 MW |
778 | /** |
779 | * Set the task ready to build notify error message | |
780 | */ | |
781 | static status_t send_notify(private_quick_mode_t *this, notify_type_t type) | |
5c6abd28 CO |
782 | { |
783 | notify_payload_t *notify; | |
784 | ||
3ecfc83c | 785 | notify = notify_payload_create_from_protocol_and_type(PLV1_NOTIFY, |
908fe163 | 786 | this->proto, type); |
5c6abd28 CO |
787 | notify->set_spi(notify, this->spi_i); |
788 | ||
e7ae90c1 | 789 | this->ike_sa->queue_task(this->ike_sa, |
2ddd45c9 | 790 | (task_t*)informational_create(this->ike_sa, notify)); |
e7ae90c1 | 791 | /* cancel all active/passive tasks in favour of informational */ |
7ce504e1 MW |
792 | this->ike_sa->flush_queue(this->ike_sa, |
793 | this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE); | |
e7ae90c1 | 794 | return ALREADY_DONE; |
5c6abd28 CO |
795 | } |
796 | ||
7d938be9 TE |
797 | /** |
798 | * Prepare a list of proposals from child_config containing only the specified | |
799 | * DH group, unless it is set to MODP_NONE. | |
800 | */ | |
801 | static linked_list_t *get_proposals(private_quick_mode_t *this, | |
802 | diffie_hellman_group_t group) | |
803 | { | |
804 | linked_list_t *list; | |
805 | proposal_t *proposal; | |
806 | enumerator_t *enumerator; | |
807 | ||
808 | list = this->config->get_proposals(this->config, FALSE); | |
809 | enumerator = list->create_enumerator(list); | |
810 | while (enumerator->enumerate(enumerator, &proposal)) | |
811 | { | |
812 | if (group != MODP_NONE) | |
813 | { | |
814 | if (!proposal->has_dh_group(proposal, group)) | |
815 | { | |
816 | list->remove_at(list, enumerator); | |
817 | proposal->destroy(proposal); | |
818 | continue; | |
819 | } | |
820 | proposal->strip_dh(proposal, group); | |
821 | } | |
822 | proposal->set_spi(proposal, this->spi_i); | |
823 | } | |
824 | enumerator->destroy(enumerator); | |
825 | ||
826 | return list; | |
827 | } | |
828 | ||
2b04aa46 MW |
829 | METHOD(task_t, build_i, status_t, |
830 | private_quick_mode_t *this, message_t *message) | |
831 | { | |
21b7db99 MW |
832 | switch (this->state) |
833 | { | |
834 | case QM_INIT: | |
835 | { | |
836 | sa_payload_t *sa_payload; | |
696fa8e0 | 837 | linked_list_t *list, *tsi, *tsr; |
908fe163 | 838 | proposal_t *proposal; |
5adf855e | 839 | diffie_hellman_group_t group; |
0ff8d20a | 840 | encap_t encap; |
21b7db99 | 841 | |
7fd7ffc6 | 842 | this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); |
47b448b8 | 843 | this->mode = this->config->get_mode(this->config); |
5351d63c MW |
844 | this->child_sa = child_sa_create( |
845 | this->ike_sa->get_my_host(this->ike_sa), | |
846 | this->ike_sa->get_other_host(this->ike_sa), | |
85b23888 MW |
847 | this->config, this->reqid, this->udp, |
848 | this->mark_in, this->mark_out); | |
5351d63c | 849 | |
00e11bce TB |
850 | if (this->udp && this->mode == MODE_TRANSPORT) |
851 | { | |
852 | /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */ | |
853 | add_nat_oa_payloads(this, message); | |
854 | } | |
5351d63c | 855 | |
749ac175 | 856 | if (this->config->has_option(this->config, OPT_IPCOMP)) |
47b448b8 | 857 | { |
44d9970f MW |
858 | this->cpi_i = this->child_sa->alloc_cpi(this->child_sa); |
859 | if (!this->cpi_i) | |
47b448b8 | 860 | { |
44d9970f MW |
861 | DBG1(DBG_IKE, "unable to allocate a CPI from kernel, " |
862 | "IPComp disabled"); | |
47b448b8 TB |
863 | } |
864 | } | |
865 | ||
908fe163 MW |
866 | list = this->config->get_proposals(this->config, MODP_NONE); |
867 | if (list->get_first(list, (void**)&proposal) == SUCCESS) | |
868 | { | |
869 | this->proto = proposal->get_protocol(proposal); | |
870 | } | |
871 | list->destroy_offset(list, offsetof(proposal_t, destroy)); | |
872 | this->spi_i = this->child_sa->alloc_spi(this->child_sa, this->proto); | |
5351d63c MW |
873 | if (!this->spi_i) |
874 | { | |
875 | DBG1(DBG_IKE, "allocating SPI from kernel failed"); | |
876 | return FAILED; | |
877 | } | |
908fe163 | 878 | |
f48e7272 TB |
879 | group = this->config->get_dh_group(this->config); |
880 | if (group != MODP_NONE) | |
881 | { | |
7d938be9 | 882 | proposal_t *proposal; |
b12c53ce | 883 | uint16_t preferred_group; |
7d938be9 TE |
884 | |
885 | proposal = this->ike_sa->get_proposal(this->ike_sa); | |
886 | proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, | |
887 | &preferred_group, NULL); | |
888 | /* try the negotiated DH group from IKE_SA */ | |
889 | list = get_proposals(this, preferred_group); | |
890 | if (list->get_count(list)) | |
891 | { | |
892 | group = preferred_group; | |
893 | } | |
894 | else | |
895 | { | |
896 | /* fall back to the first configured DH group */ | |
897 | list->destroy(list); | |
898 | list = get_proposals(this, group); | |
899 | } | |
900 | ||
f48e7272 TB |
901 | this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, |
902 | group); | |
903 | if (!this->dh) | |
904 | { | |
905 | DBG1(DBG_IKE, "configured DH group %N not supported", | |
906 | diffie_hellman_group_names, group); | |
7d938be9 | 907 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
f48e7272 TB |
908 | return FAILED; |
909 | } | |
910 | } | |
7d938be9 | 911 | else |
5351d63c | 912 | { |
7d938be9 | 913 | list = get_proposals(this, MODP_NONE); |
5351d63c | 914 | } |
5351d63c | 915 | |
cd0017d4 | 916 | get_lifetimes(this); |
0ff8d20a | 917 | encap = get_encap(this->ike_sa, this->udp); |
e174e0d4 | 918 | sa_payload = sa_payload_create_from_proposals_v1(list, |
cd0017d4 | 919 | this->lifetime, this->lifebytes, AUTH_NONE, |
0ff8d20a | 920 | this->mode, encap, this->cpi_i); |
21b7db99 MW |
921 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
922 | message->add_payload(message, &sa_payload->payload_interface); | |
923 | ||
818330aa | 924 | if (!add_nonce(this, &this->nonce_i, message)) |
21b7db99 | 925 | { |
21b7db99 MW |
926 | return FAILED; |
927 | } | |
5adf855e MW |
928 | if (group != MODP_NONE) |
929 | { | |
520d58e0 MW |
930 | if (!add_ke(this, message)) |
931 | { | |
932 | return FAILED; | |
933 | } | |
5adf855e | 934 | } |
d61f2906 MW |
935 | if (!this->tsi) |
936 | { | |
937 | this->tsi = select_ts(this, TRUE, NULL); | |
938 | } | |
939 | if (!this->tsr) | |
940 | { | |
941 | this->tsr = select_ts(this, FALSE, NULL); | |
942 | } | |
abdb82fc MW |
943 | tsi = linked_list_create_with_items(this->tsi, NULL); |
944 | tsr = linked_list_create_with_items(this->tsr, NULL); | |
696fa8e0 MW |
945 | this->tsi = this->tsr = NULL; |
946 | charon->bus->narrow(charon->bus, this->child_sa, | |
947 | NARROW_INITIATOR_PRE_AUTH, tsi, tsr); | |
948 | tsi->remove_first(tsi, (void**)&this->tsi); | |
949 | tsr->remove_first(tsr, (void**)&this->tsr); | |
950 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
951 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
c4b8539f | 952 | if (!this->tsi || !this->tsr) |
21b7db99 | 953 | { |
21b7db99 MW |
954 | return FAILED; |
955 | } | |
9cc38c8e | 956 | add_ts(this, message); |
21b7db99 MW |
957 | return NEED_MORE; |
958 | } | |
959 | case QM_NEGOTIATED: | |
960 | { | |
21b7db99 MW |
961 | return SUCCESS; |
962 | } | |
963 | default: | |
964 | return FAILED; | |
965 | } | |
2b04aa46 MW |
966 | } |
967 | ||
1755ac06 MW |
968 | /** |
969 | * Check for notify errors, return TRUE if error found | |
970 | */ | |
971 | static bool has_notify_errors(private_quick_mode_t *this, message_t *message) | |
07abb470 | 972 | { |
1755ac06 MW |
973 | enumerator_t *enumerator; |
974 | payload_t *payload; | |
975 | bool err = FALSE; | |
976 | ||
977 | enumerator = message->create_payload_enumerator(message); | |
978 | while (enumerator->enumerate(enumerator, &payload)) | |
07abb470 | 979 | { |
3ecfc83c | 980 | if (payload->get_type(payload) == PLV1_NOTIFY) |
1755ac06 MW |
981 | { |
982 | notify_payload_t *notify; | |
983 | notify_type_t type; | |
984 | ||
985 | notify = (notify_payload_t*)payload; | |
986 | type = notify->get_notify_type(notify); | |
987 | if (type < 16384) | |
988 | { | |
bf5b1d9e | 989 | |
1755ac06 MW |
990 | DBG1(DBG_IKE, "received %N error notify", |
991 | notify_type_names, type); | |
992 | err = TRUE; | |
993 | } | |
994 | else | |
995 | { | |
996 | DBG1(DBG_IKE, "received %N notify", notify_type_names, type); | |
997 | } | |
998 | } | |
07abb470 | 999 | } |
1755ac06 MW |
1000 | enumerator->destroy(enumerator); |
1001 | ||
1002 | return err; | |
07abb470 CO |
1003 | } |
1004 | ||
f56c3c53 MW |
1005 | /** |
1006 | * Check if this is a rekey for an existing CHILD_SA, reuse reqid if so | |
1007 | */ | |
1008 | static void check_for_rekeyed_child(private_quick_mode_t *this) | |
1009 | { | |
1010 | enumerator_t *enumerator, *policies; | |
1011 | traffic_selector_t *local, *remote; | |
1012 | child_sa_t *child_sa; | |
bee8b5e3 MW |
1013 | proposal_t *proposal; |
1014 | char *name; | |
f56c3c53 | 1015 | |
bee8b5e3 | 1016 | name = this->config->get_name(this->config); |
f56c3c53 MW |
1017 | enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); |
1018 | while (this->reqid == 0 && enumerator->enumerate(enumerator, &child_sa)) | |
1019 | { | |
bee8b5e3 | 1020 | if (streq(child_sa->get_name(child_sa), name)) |
f56c3c53 | 1021 | { |
bee8b5e3 MW |
1022 | proposal = child_sa->get_proposal(child_sa); |
1023 | switch (child_sa->get_state(child_sa)) | |
f56c3c53 | 1024 | { |
bee8b5e3 MW |
1025 | case CHILD_INSTALLED: |
1026 | case CHILD_REKEYING: | |
1027 | policies = child_sa->create_policy_enumerator(child_sa); | |
1028 | if (policies->enumerate(policies, &local, &remote) && | |
1029 | local->equals(local, this->tsr) && | |
1030 | remote->equals(remote, this->tsi) && | |
1031 | this->proposal->equals(this->proposal, proposal)) | |
1032 | { | |
1033 | this->reqid = child_sa->get_reqid(child_sa); | |
1034 | this->rekey = child_sa->get_spi(child_sa, TRUE); | |
85b23888 MW |
1035 | this->mark_in = child_sa->get_mark(child_sa, |
1036 | TRUE).value; | |
1037 | this->mark_out = child_sa->get_mark(child_sa, | |
1038 | FALSE).value; | |
bee8b5e3 MW |
1039 | child_sa->set_state(child_sa, CHILD_REKEYING); |
1040 | DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}", | |
246c969d MW |
1041 | child_sa->get_name(child_sa), |
1042 | child_sa->get_unique_id(child_sa)); | |
bee8b5e3 MW |
1043 | } |
1044 | policies->destroy(policies); | |
bf3bed1c TB |
1045 | break; |
1046 | case CHILD_REKEYED: | |
1047 | default: | |
1048 | break; | |
f56c3c53 | 1049 | } |
f56c3c53 MW |
1050 | } |
1051 | } | |
1052 | enumerator->destroy(enumerator); | |
1053 | } | |
1054 | ||
2b04aa46 MW |
1055 | METHOD(task_t, process_r, status_t, |
1056 | private_quick_mode_t *this, message_t *message) | |
1057 | { | |
4de361d9 TB |
1058 | if (this->mid && this->mid != message->get_message_id(message)) |
1059 | { /* not responsible for this quick mode exchange */ | |
37a22a16 | 1060 | return INVALID_ARG; |
4de361d9 TB |
1061 | } |
1062 | ||
21b7db99 MW |
1063 | switch (this->state) |
1064 | { | |
1065 | case QM_INIT: | |
1066 | { | |
1067 | sa_payload_t *sa_payload; | |
7ee37114 | 1068 | linked_list_t *tsi, *tsr, *hostsi, *hostsr, *list = NULL; |
21b7db99 | 1069 | peer_cfg_t *peer_cfg; |
b12c53ce | 1070 | uint16_t group; |
0a954d67 | 1071 | bool private, prefer_configured; |
21b7db99 | 1072 | |
a889cfe5 | 1073 | sa_payload = (sa_payload_t*)message->get_payload(message, |
3ecfc83c | 1074 | PLV1_SECURITY_ASSOCIATION); |
a889cfe5 TB |
1075 | if (!sa_payload) |
1076 | { | |
1077 | DBG1(DBG_IKE, "sa payload missing"); | |
1078 | return send_notify(this, INVALID_PAYLOAD_TYPE); | |
1079 | } | |
1080 | ||
1081 | this->mode = sa_payload->get_encap_mode(sa_payload, &this->udp); | |
1082 | ||
9cc38c8e | 1083 | if (!get_ts(this, message)) |
c4c59504 | 1084 | { |
c4b8539f | 1085 | return FAILED; |
c4c59504 | 1086 | } |
21b7db99 | 1087 | peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); |
abdb82fc MW |
1088 | tsi = linked_list_create_with_items(this->tsi, NULL); |
1089 | tsr = linked_list_create_with_items(this->tsr, NULL); | |
36431795 | 1090 | this->tsi = this->tsr = NULL; |
7ee37114 MW |
1091 | hostsi = get_dynamic_hosts(this->ike_sa, FALSE); |
1092 | hostsr = get_dynamic_hosts(this->ike_sa, TRUE); | |
21b7db99 | 1093 | this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi, |
7ee37114 MW |
1094 | hostsr, hostsi); |
1095 | hostsi->destroy(hostsi); | |
1096 | hostsr->destroy(hostsr); | |
28e3c659 MW |
1097 | if (this->config) |
1098 | { | |
1099 | this->tsi = select_ts(this, FALSE, tsi); | |
1100 | this->tsr = select_ts(this, TRUE, tsr); | |
1101 | } | |
36431795 MW |
1102 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); |
1103 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
2d38a03d TB |
1104 | if (!this->config || !this->tsi || !this->tsr || |
1105 | this->mode != this->config->get_mode(this->config)) | |
21b7db99 | 1106 | { |
5f1df0a0 MW |
1107 | DBG1(DBG_IKE, "no matching CHILD_SA config found"); |
1108 | return send_notify(this, INVALID_ID_INFORMATION); | |
21b7db99 MW |
1109 | } |
1110 | ||
749ac175 | 1111 | if (this->config->has_option(this->config, OPT_IPCOMP)) |
47b448b8 | 1112 | { |
44d9970f MW |
1113 | list = sa_payload->get_ipcomp_proposals(sa_payload, |
1114 | &this->cpi_i); | |
1115 | if (!list->get_count(list)) | |
47b448b8 | 1116 | { |
44d9970f MW |
1117 | DBG1(DBG_IKE, "expected IPComp proposal but peer did " |
1118 | "not send one, IPComp disabled"); | |
1119 | this->cpi_i = 0; | |
47b448b8 TB |
1120 | } |
1121 | } | |
1122 | if (!list || !list->get_count(list)) | |
1123 | { | |
1124 | DESTROY_IF(list); | |
47b448b8 TB |
1125 | list = sa_payload->get_proposals(sa_payload); |
1126 | } | |
44bd9b48 AS |
1127 | private = this->ike_sa->supports_extension(this->ike_sa, |
1128 | EXT_STRONGSWAN); | |
0a954d67 TB |
1129 | prefer_configured = lib->settings->get_bool(lib->settings, |
1130 | "%s.prefer_configured_proposals", TRUE, lib->ns); | |
f2ea230b | 1131 | this->proposal = this->config->select_proposal(this->config, list, |
0a954d67 | 1132 | FALSE, private, prefer_configured); |
21b7db99 | 1133 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
cd0017d4 MW |
1134 | |
1135 | get_lifetimes(this); | |
1136 | apply_lifetimes(this, sa_payload); | |
1137 | ||
21b7db99 MW |
1138 | if (!this->proposal) |
1139 | { | |
e7ae90c1 MW |
1140 | DBG1(DBG_IKE, "no matching proposal found, sending %N", |
1141 | notify_type_names, NO_PROPOSAL_CHOSEN); | |
1142 | return send_notify(this, NO_PROPOSAL_CHOSEN); | |
21b7db99 | 1143 | } |
5351d63c | 1144 | this->spi_i = this->proposal->get_spi(this->proposal); |
21b7db99 | 1145 | |
818330aa | 1146 | if (!get_nonce(this, &this->nonce_i, message)) |
21b7db99 | 1147 | { |
bf5b1d9e | 1148 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
21b7db99 | 1149 | } |
cd0017d4 | 1150 | |
5adf855e MW |
1151 | if (this->proposal->get_algorithm(this->proposal, |
1152 | DIFFIE_HELLMAN_GROUP, &group, NULL)) | |
1153 | { | |
1154 | this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, | |
1155 | group); | |
1156 | if (!this->dh) | |
1157 | { | |
1158 | DBG1(DBG_IKE, "negotiated DH group %N not supported", | |
1159 | diffie_hellman_group_names, group); | |
bf5b1d9e | 1160 | return send_notify(this, INVALID_KEY_INFORMATION); |
5adf855e MW |
1161 | } |
1162 | if (!get_ke(this, message)) | |
1163 | { | |
bf5b1d9e | 1164 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
5adf855e MW |
1165 | } |
1166 | } | |
cd0017d4 | 1167 | |
f56c3c53 MW |
1168 | check_for_rekeyed_child(this); |
1169 | ||
5351d63c MW |
1170 | this->child_sa = child_sa_create( |
1171 | this->ike_sa->get_my_host(this->ike_sa), | |
1172 | this->ike_sa->get_other_host(this->ike_sa), | |
85b23888 MW |
1173 | this->config, this->reqid, this->udp, |
1174 | this->mark_in, this->mark_out); | |
f942588f | 1175 | |
abdb82fc MW |
1176 | tsi = linked_list_create_with_items(this->tsi, NULL); |
1177 | tsr = linked_list_create_with_items(this->tsr, NULL); | |
f942588f MW |
1178 | this->tsi = this->tsr = NULL; |
1179 | charon->bus->narrow(charon->bus, this->child_sa, | |
1180 | NARROW_RESPONDER, tsr, tsi); | |
1181 | if (tsi->remove_first(tsi, (void**)&this->tsi) != SUCCESS || | |
1182 | tsr->remove_first(tsr, (void**)&this->tsr) != SUCCESS) | |
1183 | { | |
1184 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
1185 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
1186 | return send_notify(this, INVALID_ID_INFORMATION); | |
1187 | } | |
1188 | tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); | |
1189 | tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); | |
1190 | ||
21b7db99 MW |
1191 | return NEED_MORE; |
1192 | } | |
1193 | case QM_NEGOTIATED: | |
1194 | { | |
cd9bba50 | 1195 | if (has_notify_errors(this, message)) |
07abb470 | 1196 | { |
bf5b1d9e | 1197 | return SUCCESS; |
07abb470 | 1198 | } |
cd9bba50 MW |
1199 | if (message->get_exchange_type(message) == INFORMATIONAL_V1) |
1200 | { | |
1201 | if (message->get_payload(message, PLV1_DELETE)) | |
1202 | { | |
1203 | /* If the DELETE for a Quick Mode follows immediately | |
1204 | * after rekeying, we might receive it before the | |
1205 | * third completing Quick Mode message. Ignore it, as | |
1206 | * it gets handled by a separately queued delete task. */ | |
1207 | return NEED_MORE; | |
1208 | } | |
1209 | return SUCCESS; | |
1210 | } | |
5351d63c MW |
1211 | if (!install(this)) |
1212 | { | |
e91157a4 TB |
1213 | ike_sa_t *ike_sa = this->ike_sa; |
1214 | task_t *task; | |
1215 | ||
1216 | task = (task_t*)quick_delete_create(this->ike_sa, | |
767966e7 | 1217 | this->proposal->get_protocol(this->proposal), |
e91157a4 TB |
1218 | this->spi_i, TRUE, TRUE); |
1219 | /* flush_queue() destroys the current task */ | |
1220 | ike_sa->flush_queue(ike_sa, TASK_QUEUE_PASSIVE); | |
1221 | ike_sa->queue_task(ike_sa, task); | |
767966e7 | 1222 | return ALREADY_DONE; |
5351d63c | 1223 | } |
21b7db99 MW |
1224 | return SUCCESS; |
1225 | } | |
1226 | default: | |
1227 | return FAILED; | |
1228 | } | |
2b04aa46 MW |
1229 | } |
1230 | ||
1231 | METHOD(task_t, build_r, status_t, | |
1232 | private_quick_mode_t *this, message_t *message) | |
1233 | { | |
4de361d9 TB |
1234 | if (this->mid && this->mid != message->get_message_id(message)) |
1235 | { /* not responsible for this quick mode exchange */ | |
37a22a16 | 1236 | return INVALID_ARG; |
4de361d9 TB |
1237 | } |
1238 | ||
21b7db99 MW |
1239 | switch (this->state) |
1240 | { | |
1241 | case QM_INIT: | |
1242 | { | |
1243 | sa_payload_t *sa_payload; | |
0ff8d20a | 1244 | encap_t encap; |
21b7db99 | 1245 | |
908fe163 MW |
1246 | this->proto = this->proposal->get_protocol(this->proposal); |
1247 | this->spi_r = this->child_sa->alloc_spi(this->child_sa, this->proto); | |
5351d63c MW |
1248 | if (!this->spi_r) |
1249 | { | |
1250 | DBG1(DBG_IKE, "allocating SPI from kernel failed"); | |
bf5b1d9e | 1251 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
5351d63c MW |
1252 | } |
1253 | this->proposal->set_spi(this->proposal, this->spi_r); | |
1254 | ||
47b448b8 TB |
1255 | if (this->cpi_i) |
1256 | { | |
1257 | this->cpi_r = this->child_sa->alloc_cpi(this->child_sa); | |
1258 | if (!this->cpi_r) | |
1259 | { | |
1260 | DBG1(DBG_IKE, "unable to allocate a CPI from " | |
1261 | "kernel, IPComp disabled"); | |
1262 | return send_notify(this, NO_PROPOSAL_CHOSEN); | |
1263 | } | |
1264 | } | |
1265 | ||
7fd7ffc6 | 1266 | if (this->udp && this->mode == MODE_TRANSPORT) |
29b0cb32 | 1267 | { |
3bf0be6b | 1268 | /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */ |
9f80110b | 1269 | add_nat_oa_payloads(this, message); |
29b0cb32 TB |
1270 | } |
1271 | ||
0ff8d20a | 1272 | encap = get_encap(this->ike_sa, this->udp); |
e174e0d4 | 1273 | sa_payload = sa_payload_create_from_proposal_v1(this->proposal, |
cd0017d4 | 1274 | this->lifetime, this->lifebytes, AUTH_NONE, |
0ff8d20a | 1275 | this->mode, encap, this->cpi_r); |
21b7db99 MW |
1276 | message->add_payload(message, &sa_payload->payload_interface); |
1277 | ||
818330aa | 1278 | if (!add_nonce(this, &this->nonce_r, message)) |
21b7db99 | 1279 | { |
21b7db99 MW |
1280 | return FAILED; |
1281 | } | |
5adf855e MW |
1282 | if (this->dh) |
1283 | { | |
520d58e0 MW |
1284 | if (!add_ke(this, message)) |
1285 | { | |
1286 | return FAILED; | |
1287 | } | |
5adf855e MW |
1288 | } |
1289 | ||
9cc38c8e | 1290 | add_ts(this, message); |
21b7db99 | 1291 | |
21b7db99 | 1292 | this->state = QM_NEGOTIATED; |
4de361d9 | 1293 | this->mid = message->get_message_id(message); |
21b7db99 MW |
1294 | return NEED_MORE; |
1295 | } | |
cd9bba50 MW |
1296 | case QM_NEGOTIATED: |
1297 | if (message->get_exchange_type(message) == INFORMATIONAL_V1) | |
1298 | { | |
1299 | /* skip INFORMATIONAL response if we received a INFORMATIONAL | |
1300 | * delete, see process_r() */ | |
1301 | return ALREADY_DONE; | |
1302 | } | |
1303 | /* fall */ | |
21b7db99 MW |
1304 | default: |
1305 | return FAILED; | |
1306 | } | |
2b04aa46 MW |
1307 | } |
1308 | ||
1309 | METHOD(task_t, process_i, status_t, | |
1310 | private_quick_mode_t *this, message_t *message) | |
1311 | { | |
21b7db99 MW |
1312 | switch (this->state) |
1313 | { | |
1314 | case QM_INIT: | |
1315 | { | |
1316 | sa_payload_t *sa_payload; | |
47b448b8 | 1317 | linked_list_t *list = NULL; |
44bd9b48 | 1318 | bool private; |
21b7db99 MW |
1319 | |
1320 | sa_payload = (sa_payload_t*)message->get_payload(message, | |
3ecfc83c | 1321 | PLV1_SECURITY_ASSOCIATION); |
21b7db99 MW |
1322 | if (!sa_payload) |
1323 | { | |
1324 | DBG1(DBG_IKE, "sa payload missing"); | |
bf5b1d9e | 1325 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
21b7db99 | 1326 | } |
47b448b8 TB |
1327 | if (this->cpi_i) |
1328 | { | |
1329 | list = sa_payload->get_ipcomp_proposals(sa_payload, | |
1330 | &this->cpi_r); | |
3451ecd7 TB |
1331 | if (!list->get_count(list)) |
1332 | { | |
2db6d5b8 | 1333 | DBG1(DBG_IKE, "peer did not accept our IPComp proposal, " |
3451ecd7 TB |
1334 | "IPComp disabled"); |
1335 | this->cpi_i = 0; | |
1336 | } | |
47b448b8 TB |
1337 | } |
1338 | if (!list || !list->get_count(list)) | |
1339 | { | |
1340 | DESTROY_IF(list); | |
47b448b8 TB |
1341 | list = sa_payload->get_proposals(sa_payload); |
1342 | } | |
44bd9b48 AS |
1343 | private = this->ike_sa->supports_extension(this->ike_sa, |
1344 | EXT_STRONGSWAN); | |
f2ea230b TB |
1345 | this->proposal = this->config->select_proposal(this->config, list, |
1346 | FALSE, private, TRUE); | |
21b7db99 MW |
1347 | list->destroy_offset(list, offsetof(proposal_t, destroy)); |
1348 | if (!this->proposal) | |
1349 | { | |
1350 | DBG1(DBG_IKE, "no matching proposal found"); | |
bf5b1d9e | 1351 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
21b7db99 | 1352 | } |
5351d63c MW |
1353 | this->spi_r = this->proposal->get_spi(this->proposal); |
1354 | ||
cd0017d4 MW |
1355 | apply_lifetimes(this, sa_payload); |
1356 | ||
818330aa | 1357 | if (!get_nonce(this, &this->nonce_r, message)) |
21b7db99 | 1358 | { |
bf5b1d9e | 1359 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
21b7db99 | 1360 | } |
5adf855e MW |
1361 | if (this->dh && !get_ke(this, message)) |
1362 | { | |
bf5b1d9e | 1363 | return send_notify(this, INVALID_KEY_INFORMATION); |
5adf855e | 1364 | } |
9cc38c8e | 1365 | if (!get_ts(this, message)) |
c4b8539f | 1366 | { |
bf5b1d9e | 1367 | return send_notify(this, INVALID_PAYLOAD_TYPE); |
c4b8539f | 1368 | } |
5351d63c MW |
1369 | if (!install(this)) |
1370 | { | |
bf5b1d9e | 1371 | return send_notify(this, NO_PROPOSAL_CHOSEN); |
5351d63c | 1372 | } |
21b7db99 MW |
1373 | this->state = QM_NEGOTIATED; |
1374 | return NEED_MORE; | |
1375 | } | |
1376 | default: | |
1377 | return FAILED; | |
1378 | } | |
2b04aa46 MW |
1379 | } |
1380 | ||
1381 | METHOD(task_t, get_type, task_type_t, | |
1382 | private_quick_mode_t *this) | |
1383 | { | |
1384 | return TASK_QUICK_MODE; | |
1385 | } | |
1386 | ||
b12c53ce | 1387 | METHOD(quick_mode_t, get_mid, uint32_t, |
4de361d9 TB |
1388 | private_quick_mode_t *this) |
1389 | { | |
1390 | return this->mid; | |
1391 | } | |
1392 | ||
14dc7941 | 1393 | METHOD(quick_mode_t, use_reqid, void, |
b12c53ce | 1394 | private_quick_mode_t *this, uint32_t reqid) |
14dc7941 MW |
1395 | { |
1396 | this->reqid = reqid; | |
1397 | } | |
1398 | ||
85b23888 MW |
1399 | METHOD(quick_mode_t, use_marks, void, |
1400 | private_quick_mode_t *this, u_int in, u_int out) | |
1401 | { | |
1402 | this->mark_in = in; | |
1403 | this->mark_out = out; | |
1404 | } | |
1405 | ||
669d8bde | 1406 | METHOD(quick_mode_t, rekey, void, |
b12c53ce | 1407 | private_quick_mode_t *this, uint32_t spi) |
669d8bde MW |
1408 | { |
1409 | this->rekey = spi; | |
1410 | } | |
1411 | ||
2b04aa46 MW |
1412 | METHOD(task_t, migrate, void, |
1413 | private_quick_mode_t *this, ike_sa_t *ike_sa) | |
1414 | { | |
bce22af2 MW |
1415 | chunk_free(&this->nonce_i); |
1416 | chunk_free(&this->nonce_r); | |
1417 | DESTROY_IF(this->tsi); | |
1418 | DESTROY_IF(this->tsr); | |
1419 | DESTROY_IF(this->proposal); | |
1420 | DESTROY_IF(this->child_sa); | |
1421 | DESTROY_IF(this->dh); | |
1422 | ||
2b04aa46 | 1423 | this->ike_sa = ike_sa; |
bce22af2 MW |
1424 | this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); |
1425 | this->state = QM_INIT; | |
4de361d9 | 1426 | this->mid = 0; |
bce22af2 MW |
1427 | this->tsi = NULL; |
1428 | this->tsr = NULL; | |
1429 | this->proposal = NULL; | |
1430 | this->child_sa = NULL; | |
1431 | this->dh = NULL; | |
1432 | this->spi_i = 0; | |
1433 | this->spi_r = 0; | |
85b23888 MW |
1434 | this->mark_in = 0; |
1435 | this->mark_out = 0; | |
bce22af2 MW |
1436 | |
1437 | if (!this->initiator) | |
1438 | { | |
1439 | DESTROY_IF(this->config); | |
1440 | this->config = NULL; | |
1441 | } | |
2b04aa46 MW |
1442 | } |
1443 | ||
1444 | METHOD(task_t, destroy, void, | |
1445 | private_quick_mode_t *this) | |
1446 | { | |
1447 | chunk_free(&this->nonce_i); | |
1448 | chunk_free(&this->nonce_r); | |
1449 | DESTROY_IF(this->tsi); | |
1450 | DESTROY_IF(this->tsr); | |
1451 | DESTROY_IF(this->proposal); | |
1452 | DESTROY_IF(this->child_sa); | |
1453 | DESTROY_IF(this->config); | |
5adf855e | 1454 | DESTROY_IF(this->dh); |
2b04aa46 MW |
1455 | free(this); |
1456 | } | |
1457 | ||
1458 | /* | |
1459 | * Described in header. | |
1460 | */ | |
1461 | quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, | |
1462 | traffic_selector_t *tsi, traffic_selector_t *tsr) | |
1463 | { | |
1464 | private_quick_mode_t *this; | |
1465 | ||
1466 | INIT(this, | |
1467 | .public = { | |
1468 | .task = { | |
1469 | .get_type = _get_type, | |
1470 | .migrate = _migrate, | |
1471 | .destroy = _destroy, | |
1472 | }, | |
4de361d9 | 1473 | .get_mid = _get_mid, |
14dc7941 | 1474 | .use_reqid = _use_reqid, |
85b23888 | 1475 | .use_marks = _use_marks, |
669d8bde | 1476 | .rekey = _rekey, |
2b04aa46 MW |
1477 | }, |
1478 | .ike_sa = ike_sa, | |
5351d63c | 1479 | .initiator = config != NULL, |
2b04aa46 | 1480 | .config = config, |
5351d63c | 1481 | .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa), |
2b04aa46 | 1482 | .state = QM_INIT, |
d61f2906 MW |
1483 | .tsi = tsi ? tsi->clone(tsi) : NULL, |
1484 | .tsr = tsr ? tsr->clone(tsr) : NULL, | |
908fe163 | 1485 | .proto = PROTO_ESP, |
2f3c08d2 TB |
1486 | .delete = lib->settings->get_bool(lib->settings, |
1487 | "%s.delete_rekeyed", FALSE, lib->ns), | |
2b04aa46 MW |
1488 | ); |
1489 | ||
1490 | if (config) | |
1491 | { | |
1492 | this->public.task.build = _build_i; | |
1493 | this->public.task.process = _process_i; | |
1494 | } | |
1495 | else | |
1496 | { | |
1497 | this->public.task.build = _build_r; | |
1498 | this->public.task.process = _process_r; | |
1499 | } | |
1500 | ||
1501 | return &this->public; | |
1502 | } |