]>
Commit | Line | Data |
---|---|---|
7ba38761 JH |
1 | /** |
2 | * @file ike_sa.c | |
79538669 | 3 | * |
df3c59d0 | 4 | * @brief Implementation of ike_sa_t. |
79538669 | 5 | * |
7ba38761 JH |
6 | */ |
7 | ||
8 | /* | |
1396815a | 9 | * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger |
c71d53ba MW |
10 | * Copyright (C) 2005-2006 Martin Willi |
11 | * Copyright (C) 2005 Jan Hutter | |
7ba38761 JH |
12 | * Hochschule fuer Technik Rapperswil |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify it | |
15 | * under the terms of the GNU General Public License as published by the | |
16 | * Free Software Foundation; either version 2 of the License, or (at your | |
17 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, but | |
20 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
21 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
22 | * for more details. | |
23 | */ | |
1396815a MW |
24 | |
25 | #include <sys/time.h> | |
5113680f | 26 | #include <string.h> |
7ba38761 | 27 | |
88878242 MW |
28 | #include "ike_sa.h" |
29 | ||
021c2322 | 30 | #include <types.h> |
0e96f7d8 | 31 | #include <daemon.h> |
021c2322 | 32 | #include <definitions.h> |
021c2322 MW |
33 | #include <utils/linked_list.h> |
34 | #include <utils/logger_manager.h> | |
68621281 MW |
35 | #include <crypto/diffie_hellman.h> |
36 | #include <crypto/prf_plus.h> | |
37 | #include <crypto/crypters/crypter.h> | |
1396815a | 38 | #include <crypto/hashers/hasher.h> |
4a962238 MW |
39 | #include <encoding/payloads/sa_payload.h> |
40 | #include <encoding/payloads/nonce_payload.h> | |
41 | #include <encoding/payloads/ke_payload.h> | |
e9c0ca15 | 42 | #include <encoding/payloads/delete_payload.h> |
4a962238 MW |
43 | #include <encoding/payloads/transform_substructure.h> |
44 | #include <encoding/payloads/transform_attribute.h> | |
8d77edde | 45 | #include <encoding/payloads/ts_payload.h> |
3dd3c5f3 MW |
46 | #include <sa/transactions/transaction.h> |
47 | #include <sa/transactions/ike_sa_init.h> | |
48 | #include <sa/transactions/delete_ike_sa.h> | |
698d7749 MW |
49 | #include <sa/transactions/create_child_sa.h> |
50 | #include <sa/transactions/delete_child_sa.h> | |
3dd3c5f3 | 51 | #include <sa/transactions/dead_peer_detection.h> |
0df63d6b | 52 | #include <queues/jobs/retransmit_request_job.h> |
e314700c | 53 | #include <queues/jobs/delete_established_ike_sa_job.h> |
4a5bba25 | 54 | #include <queues/jobs/delete_half_open_ike_sa_job.h> |
3dd3c5f3 MW |
55 | #include <queues/jobs/send_dpd_job.h> |
56 | #include <queues/jobs/send_keepalive_job.h> | |
7ba38761 JH |
57 | |
58 | ||
3dd3c5f3 MW |
59 | /** |
60 | * String mappings for ike_sa_state_t. | |
61 | */ | |
62 | mapping_t ike_sa_state_m[] = { | |
bcb95ced MW |
63 | {IKE_CREATED, "CREATED"}, |
64 | {IKE_CONNECTING, "CONNECTING"}, | |
65 | {IKE_ESTABLISHED, "ESTABLISHED"}, | |
66 | {IKE_DELETING, "DELETING"}, | |
3dd3c5f3 MW |
67 | {MAPPING_END, NULL} |
68 | }; | |
501a41b9 | 69 | |
7ba38761 | 70 | |
aad398a7 JH |
71 | typedef struct private_ike_sa_t private_ike_sa_t; |
72 | ||
73 | /** | |
74 | * Private data of an ike_sa_t object. | |
75 | */ | |
76 | struct private_ike_sa_t { | |
77 | ||
78 | /** | |
3dd3c5f3 | 79 | * Public members |
2f89902d | 80 | */ |
3dd3c5f3 | 81 | ike_sa_t public; |
2f89902d | 82 | |
aad398a7 | 83 | /** |
a374d1ee | 84 | * Identifier for the current IKE_SA. |
aad398a7 JH |
85 | */ |
86 | ike_sa_id_t *ike_sa_id; | |
8d77edde | 87 | |
aad398a7 | 88 | /** |
8dfbe71b | 89 | * Current state of the IKE_SA |
aad398a7 | 90 | */ |
8dfbe71b | 91 | ike_sa_state_t state; |
a9428251 JH |
92 | |
93 | /** | |
8dfbe71b | 94 | * Name of the connection used by this IKE_SA |
a9428251 | 95 | */ |
8dfbe71b MW |
96 | char *name; |
97 | ||
98 | /** | |
99 | * Address of local host | |
100 | */ | |
101 | host_t *my_host; | |
102 | ||
103 | /** | |
104 | * Address of remote host | |
105 | */ | |
106 | host_t *other_host; | |
107 | ||
108 | /** | |
109 | * Identification used for us | |
110 | */ | |
111 | identification_t *my_id; | |
a9428251 JH |
112 | |
113 | /** | |
8dfbe71b | 114 | * Identification used for other |
a9428251 | 115 | */ |
8dfbe71b | 116 | identification_t *other_id; |
a374d1ee | 117 | |
aad398a7 | 118 | /** |
8dfbe71b | 119 | * Linked List containing the child sa's of the current IKE_SA. |
aad398a7 | 120 | */ |
8dfbe71b | 121 | linked_list_t *child_sas; |
aad398a7 JH |
122 | |
123 | /** | |
3dd3c5f3 | 124 | * crypter for inbound traffic |
aad398a7 | 125 | */ |
3dd3c5f3 | 126 | crypter_t *crypter_in; |
aad398a7 JH |
127 | |
128 | /** | |
3dd3c5f3 | 129 | * crypter for outbound traffic |
aad398a7 | 130 | */ |
3dd3c5f3 | 131 | crypter_t *crypter_out; |
aad398a7 JH |
132 | |
133 | /** | |
3dd3c5f3 | 134 | * Signer for inbound traffic |
aad398a7 | 135 | */ |
3dd3c5f3 | 136 | signer_t *signer_in; |
aad398a7 JH |
137 | |
138 | /** | |
3dd3c5f3 | 139 | * Signer for outbound traffic |
aad398a7 | 140 | */ |
3dd3c5f3 | 141 | signer_t *signer_out; |
aad398a7 JH |
142 | |
143 | /** | |
ce461bbd | 144 | * Multi purpose prf, set key, use it, forget it |
aad398a7 JH |
145 | */ |
146 | prf_t *prf; | |
147 | ||
5b97779f MW |
148 | /** |
149 | * Prf function for derivating keymat child SAs | |
5b97779f MW |
150 | */ |
151 | prf_t *child_prf; | |
152 | ||
aad398a7 | 153 | /** |
ce461bbd | 154 | * PRF, with key set to pi_key, used for authentication |
aad398a7 | 155 | */ |
ce461bbd | 156 | prf_t *prf_auth_i; |
aad398a7 | 157 | |
ce461bbd MW |
158 | /** |
159 | * PRF, with key set to pr_key, used for authentication | |
160 | */ | |
161 | prf_t *prf_auth_r; | |
0df63d6b | 162 | |
aad398a7 | 163 | /** |
a374d1ee | 164 | * A logger for this IKE_SA. |
aad398a7 JH |
165 | */ |
166 | logger_t *logger; | |
1396815a MW |
167 | |
168 | /** | |
169 | * NAT hasher. | |
170 | */ | |
171 | hasher_t *nat_hasher; | |
172 | ||
173 | /** | |
174 | * NAT status of local host. | |
175 | */ | |
176 | bool nat_here; | |
177 | ||
178 | /** | |
179 | * NAT status of remote host. | |
180 | */ | |
181 | bool nat_there; | |
3dd3c5f3 MW |
182 | |
183 | /** | |
184 | * message ID for next outgoung request | |
185 | */ | |
186 | u_int32_t message_id_out; | |
1396815a MW |
187 | |
188 | /** | |
2f89902d | 189 | * Timestamp of last IKE message received on this SA |
1396815a | 190 | */ |
3dd3c5f3 | 191 | time_t time_inbound; |
2f89902d MW |
192 | |
193 | /** | |
194 | * Timestamp of last IKE message sent on this SA | |
195 | */ | |
3dd3c5f3 MW |
196 | time_t time_outbound; |
197 | ||
198 | /** | |
199 | * List of queued transactions to process | |
200 | */ | |
201 | linked_list_t *transaction_queue; | |
202 | ||
203 | /** | |
204 | * Transaction currently initiated | |
205 | * (only one supported yet, window size = 1) | |
206 | */ | |
207 | transaction_t *transaction_out; | |
208 | ||
209 | /** | |
210 | * last transaction initiated by peer processed. | |
211 | * (only one supported yet, window size = 1) | |
212 | * Stored for retransmission. | |
1396815a | 213 | */ |
3dd3c5f3 MW |
214 | transaction_t *transaction_in; |
215 | ||
216 | /** | |
217 | * Next incoming transaction expected. Used to | |
218 | * do multi transaction operations. | |
219 | */ | |
220 | transaction_t *transaction_in_next; | |
aad398a7 JH |
221 | }; |
222 | ||
0b2abb8c | 223 | /** |
3dd3c5f3 | 224 | * get the time of the latest traffic processed by the kernel |
0b2abb8c | 225 | */ |
8dfbe71b | 226 | static time_t get_kernel_time(private_ike_sa_t* this, bool inbound) |
3a8f9f44 | 227 | { |
3dd3c5f3 MW |
228 | iterator_t *iterator; |
229 | child_sa_t *child_sa; | |
230 | time_t latest = 0, use_time; | |
16b9a73c | 231 | |
3dd3c5f3 MW |
232 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); |
233 | while (iterator->iterate(iterator, (void**)&child_sa)) | |
234 | { | |
235 | if (child_sa->get_use_time(child_sa, inbound, &use_time) == SUCCESS) | |
236 | { | |
237 | latest = max(latest, use_time); | |
238 | } | |
239 | } | |
240 | iterator->destroy(iterator); | |
5534ee84 | 241 | |
3dd3c5f3 | 242 | return latest; |
a9428251 JH |
243 | } |
244 | ||
aad398a7 | 245 | /** |
3dd3c5f3 | 246 | * get the time of the latest received traffice |
aad398a7 | 247 | */ |
3dd3c5f3 | 248 | static time_t get_time_inbound(private_ike_sa_t *this) |
aad398a7 | 249 | { |
8dfbe71b | 250 | return max(this->time_inbound, get_kernel_time(this, TRUE)); |
aad398a7 JH |
251 | } |
252 | ||
0fdc3c7f | 253 | /** |
3dd3c5f3 | 254 | * get the time of the latest sent traffic |
0fdc3c7f | 255 | */ |
3dd3c5f3 | 256 | static time_t get_time_outbound(private_ike_sa_t *this) |
0fdc3c7f | 257 | { |
8dfbe71b | 258 | return max(this->time_outbound, get_kernel_time(this, FALSE)); |
0fdc3c7f JH |
259 | } |
260 | ||
8dfbe71b MW |
261 | /** |
262 | * Implementation of ike_sa_t.get_name. | |
263 | */ | |
264 | static char *get_name(private_ike_sa_t *this) | |
265 | { | |
266 | return this->name; | |
267 | } | |
268 | ||
269 | /** | |
270 | * Implementation of ike_sa_t.set_name. | |
271 | */ | |
272 | static void set_name(private_ike_sa_t *this, char* name) | |
273 | { | |
274 | free(this->name); | |
275 | this->name = strdup(name); | |
276 | } | |
277 | ||
278 | /** | |
279 | * Implementation of ike_sa_t.get_my_host. | |
280 | */ | |
281 | static host_t *get_my_host(private_ike_sa_t *this) | |
282 | { | |
283 | return this->my_host; | |
284 | } | |
285 | ||
286 | /** | |
287 | * Implementation of ike_sa_t.get_other_host. | |
288 | */ | |
289 | static host_t *get_other_host(private_ike_sa_t *this) | |
290 | { | |
291 | return this->other_host; | |
292 | } | |
5b97779f | 293 | |
0fdc3c7f | 294 | /** |
3dd3c5f3 | 295 | * Update connection host, as addresses may change (NAT) |
0fdc3c7f | 296 | */ |
3dd3c5f3 | 297 | static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) |
0fdc3c7f | 298 | { |
3dd3c5f3 MW |
299 | /* |
300 | * Quoting RFC 4306: | |
301 | * | |
302 | * 2.11. Address and Port Agility | |
303 | * | |
304 | * IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and | |
305 | * AH associations for the same IP addresses it runs over. The IP | |
306 | * addresses and ports in the outer header are, however, not themselves | |
307 | * cryptographically protected, and IKE is designed to work even through | |
308 | * Network Address Translation (NAT) boxes. An implementation MUST | |
309 | * accept incoming requests even if the source port is not 500 or 4500, | |
310 | * and MUST respond to the address and port from which the request was | |
311 | * received. It MUST specify the address and port at which the request | |
312 | * was received as the source address and port in the response. IKE | |
313 | * functions identically over IPv4 or IPv6. | |
314 | * | |
315 | * [...] | |
316 | * | |
317 | * There are cases where a NAT box decides to remove mappings that | |
318 | * are still alive (for example, the keepalive interval is too long, | |
319 | * or the NAT box is rebooted). To recover in these cases, hosts | |
320 | * that are not behind a NAT SHOULD send all packets (including | |
321 | * retransmission packets) to the IP address and port from the last | |
322 | * valid authenticated packet from the other end (i.e., dynamically | |
323 | * update the address). A host behind a NAT SHOULD NOT do this | |
324 | * because it opens a DoS attack possibility. Any authenticated IKE | |
325 | * packet or any authenticated UDP-encapsulated ESP packet can be | |
326 | * used to detect that the IP address or the port has changed. | |
327 | */ | |
3dd3c5f3 MW |
328 | iterator_t *iterator = NULL; |
329 | child_sa_t *child_sa = NULL; | |
8dfbe71b MW |
330 | host_diff_t my_diff, other_diff; |
331 | ||
45f76a7d MW |
332 | if (this->my_host->is_anyaddr(this->my_host) || |
333 | this->other_host->is_anyaddr(this->other_host)) | |
8dfbe71b MW |
334 | { |
335 | /* on first received message */ | |
45f76a7d | 336 | this->my_host->destroy(this->my_host); |
8dfbe71b | 337 | this->my_host = me->clone(me); |
45f76a7d | 338 | this->other_host->destroy(this->other_host); |
8dfbe71b MW |
339 | this->other_host = other->clone(other); |
340 | return; | |
341 | } | |
342 | ||
343 | my_diff = me->get_differences(me, this->my_host); | |
344 | other_diff = other->get_differences(other, this->other_host); | |
345 | ||
346 | if (!my_diff && !other_diff) | |
3dd3c5f3 MW |
347 | { |
348 | return; | |
349 | } | |
350 | ||
8dfbe71b | 351 | if (my_diff) |
3dd3c5f3 | 352 | { |
8dfbe71b MW |
353 | this->my_host->destroy(this->my_host); |
354 | this->my_host = me->clone(me); | |
3dd3c5f3 MW |
355 | } |
356 | ||
357 | if (!this->nat_here) | |
358 | { | |
359 | /* update without restrictions if we are not NATted */ | |
8dfbe71b | 360 | if (other_diff) |
3dd3c5f3 | 361 | { |
8dfbe71b MW |
362 | this->other_host->destroy(this->other_host); |
363 | this->other_host = other->clone(other); | |
3dd3c5f3 MW |
364 | } |
365 | } | |
366 | else | |
367 | { | |
368 | /* if we are natted, only port may change */ | |
8dfbe71b | 369 | if (other_diff & HOST_DIFF_ADDR) |
3dd3c5f3 MW |
370 | { |
371 | return; | |
372 | } | |
8dfbe71b | 373 | else if (other_diff & HOST_DIFF_PORT) |
3dd3c5f3 | 374 | { |
8dfbe71b | 375 | this->other_host->set_port(this->other_host, other->get_port(other)); |
3dd3c5f3 MW |
376 | } |
377 | } | |
378 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); | |
379 | while (iterator->iterate(iterator, (void**)&child_sa)) | |
380 | { | |
8dfbe71b MW |
381 | child_sa->update_hosts(child_sa, this->my_host, this->other_host, |
382 | my_diff, other_diff); | |
3dd3c5f3 MW |
383 | /* TODO: what to do if update fails? Delete CHILD_SA? */ |
384 | } | |
385 | iterator->destroy(iterator); | |
8d77edde MW |
386 | } |
387 | ||
388 | /** | |
3dd3c5f3 | 389 | * send a request and schedule retransmission |
8d77edde | 390 | */ |
3dd3c5f3 | 391 | static status_t transmit_request(private_ike_sa_t *this) |
8d77edde | 392 | { |
3dd3c5f3 MW |
393 | message_t *request; |
394 | packet_t *packet; | |
395 | status_t status; | |
396 | retransmit_request_job_t *job; | |
397 | u_int32_t transmitted; | |
398 | u_int32_t timeout; | |
399 | transaction_t *transaction = this->transaction_out; | |
400 | u_int32_t message_id = transaction->get_message_id(transaction); | |
401 | ||
402 | transmitted = transaction->requested(transaction); | |
403 | timeout = charon->configuration->get_retransmit_timeout(charon->configuration, | |
404 | transmitted); | |
405 | if (timeout == 0) | |
406 | { | |
407 | this->logger->log(this->logger, ERROR, | |
408 | "giving up after %d retransmits, deleting IKE_SA", | |
409 | transmitted - 1); | |
410 | return DESTROY_ME; | |
411 | } | |
412 | ||
413 | status = transaction->get_request(transaction, &request); | |
414 | if (status != SUCCESS) | |
415 | { | |
416 | return status; | |
417 | } | |
418 | /* if we retransmit, the request is already generated */ | |
419 | if (transmitted == 0) | |
420 | { | |
421 | status = request->generate(request, this->crypter_out, this->signer_out, &packet); | |
422 | if (status != SUCCESS) | |
423 | { | |
92ee45a0 MW |
424 | this->logger->log(this->logger, ERROR, |
425 | "request generation failed. transaction discarded"); | |
3dd3c5f3 MW |
426 | return FAILED; |
427 | } | |
428 | } | |
429 | else | |
430 | { | |
431 | this->logger->log(this->logger, CONTROL, | |
432 | "sending retransmit %d for %s request with message ID %d", | |
433 | transmitted, | |
434 | mapping_find(exchange_type_m, request->get_exchange_type(request)), | |
435 | message_id); | |
436 | packet = request->get_packet(request); | |
437 | } | |
438 | /* finally send */ | |
439 | charon->send_queue->add(charon->send_queue, packet); | |
440 | this->time_outbound = time(NULL); | |
441 | ||
442 | /* schedule retransmission job */ | |
443 | job = retransmit_request_job_create(message_id, this->ike_sa_id); | |
444 | charon->event_queue->add_relative(charon->event_queue, (job_t*)job, timeout); | |
445 | return SUCCESS; | |
8d77edde MW |
446 | } |
447 | ||
448 | /** | |
3dd3c5f3 | 449 | * Implementation of ike_sa.retransmit_request. |
8d77edde | 450 | */ |
3dd3c5f3 | 451 | static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id) |
8d77edde | 452 | { |
3dd3c5f3 MW |
453 | if (this->transaction_out == NULL || |
454 | this->transaction_out->get_message_id(this->transaction_out) != message_id) | |
455 | { | |
456 | /* no retransmit necessary, transaction did already complete */ | |
457 | return SUCCESS; | |
458 | } | |
459 | return transmit_request(this); | |
8d77edde MW |
460 | } |
461 | ||
1396815a | 462 | /** |
3dd3c5f3 | 463 | * Check for transactions in the queue and initiate the first transaction found. |
1396815a | 464 | */ |
3dd3c5f3 | 465 | static status_t process_transaction_queue(private_ike_sa_t *this) |
1396815a | 466 | { |
3dd3c5f3 MW |
467 | if (this->transaction_out) |
468 | { | |
469 | /* already a transaction in progress */ | |
470 | return SUCCESS; | |
471 | } | |
472 | ||
473 | while (TRUE) | |
474 | { | |
475 | if (this->transaction_queue->remove_first(this->transaction_queue, | |
476 | (void**)&this->transaction_out) != SUCCESS) | |
477 | { | |
478 | /* transaction queue empty */ | |
479 | return SUCCESS; | |
480 | } | |
481 | switch (transmit_request(this)) | |
482 | { | |
483 | case SUCCESS: | |
484 | return SUCCESS; | |
485 | case DESTROY_ME: | |
486 | /* critical, IKE_SA unusable, destroy immediately */ | |
487 | this->logger->log(this->logger, ERROR, | |
488 | "transaction initiaton failed, deleting IKE_SA"); | |
489 | return DESTROY_ME; | |
490 | default: | |
491 | /* discard transaction, process next one */ | |
492 | this->logger->log(this->logger, ERROR, | |
493 | "transaction initiation failed, discarded"); | |
494 | this->transaction_out->destroy(this->transaction_out); | |
495 | this->transaction_out = NULL; | |
496 | /* handle next transaction */ | |
497 | continue; | |
498 | } | |
499 | } | |
1396815a MW |
500 | } |
501 | ||
8d77edde | 502 | /** |
3dd3c5f3 | 503 | * Queue a new transaction and execute the next outstanding transaction |
8d77edde | 504 | */ |
3dd3c5f3 | 505 | static status_t queue_transaction(private_ike_sa_t *this, transaction_t *transaction, bool prefer) |
8d77edde | 506 | { |
3dd3c5f3 MW |
507 | /* inject next transaction */ |
508 | if (transaction) | |
8d77edde | 509 | { |
3dd3c5f3 MW |
510 | if (prefer) |
511 | { | |
512 | this->transaction_queue->insert_first(this->transaction_queue, transaction); | |
513 | } | |
514 | else | |
515 | { | |
516 | this->transaction_queue->insert_last(this->transaction_queue, transaction); | |
517 | } | |
8d77edde | 518 | } |
3dd3c5f3 MW |
519 | /* process a transaction */ |
520 | return process_transaction_queue(this); | |
8d77edde | 521 | } |
aebb38a0 | 522 | |
aad398a7 | 523 | /** |
3dd3c5f3 | 524 | * process an incoming request. |
aad398a7 | 525 | */ |
3dd3c5f3 | 526 | static status_t process_request(private_ike_sa_t *this, message_t *request) |
aad398a7 | 527 | { |
3dd3c5f3 MW |
528 | transaction_t *last, *current = NULL; |
529 | message_t *response; | |
530 | packet_t *packet; | |
531 | u_int32_t request_mid; | |
532 | status_t status; | |
b9d9f188 | 533 | |
3dd3c5f3 MW |
534 | request_mid = request->get_message_id(request); |
535 | last = this->transaction_in; | |
536 | ||
537 | /* check if message ID is correct */ | |
538 | if (last) | |
aad398a7 | 539 | { |
3dd3c5f3 MW |
540 | u_int32_t last_mid = last->get_message_id(last); |
541 | ||
542 | if (last_mid == request_mid) | |
543 | { | |
544 | /* retransmit detected */ | |
545 | this->logger->log(this->logger, ERROR, | |
546 | "received retransmitted request for message ID %d, retransmitting response", | |
547 | request_mid); | |
548 | last->get_response(last, request, &response, &this->transaction_in_next); | |
549 | packet = response->get_packet(response); | |
550 | charon->send_queue->add(charon->send_queue, packet); | |
551 | this->time_outbound = time(NULL); | |
552 | return SUCCESS; | |
553 | } | |
554 | ||
555 | if (last_mid > request_mid) | |
556 | { | |
557 | /* something seriously wrong here, message id may not decrease */ | |
558 | this->logger->log(this->logger, ERROR, | |
559 | "received request with message ID %d, excepted %d, ingored", | |
560 | request_mid, last_mid + 1); | |
561 | return FAILED; | |
562 | } | |
563 | /* we allow jumps in message IDs, as long as they are incremental */ | |
564 | if (last_mid + 1 < request_mid) | |
565 | { | |
566 | this->logger->log(this->logger, ERROR, | |
567 | "received request with message ID %d, excepted %d", | |
568 | request_mid, last_mid + 1); | |
569 | } | |
aad398a7 | 570 | } |
3dd3c5f3 | 571 | else |
b9d9f188 | 572 | { |
3dd3c5f3 MW |
573 | if (request_mid != 0) |
574 | { | |
575 | /* warn, but allow it */ | |
576 | this->logger->log(this->logger, CONTROL, | |
577 | "first received request has message ID %d, excepted 0", | |
578 | request_mid); | |
579 | } | |
b9d9f188 | 580 | } |
ce461bbd | 581 | |
3dd3c5f3 MW |
582 | /* check if we already have a pre-created transaction for this request */ |
583 | if (this->transaction_in_next) | |
ce461bbd | 584 | { |
bcb95ced | 585 | current = this->transaction_in_next; |
3dd3c5f3 | 586 | this->transaction_in_next = NULL; |
ce461bbd | 587 | } |
bcb95ced | 588 | else |
b9d9f188 | 589 | { |
3dd3c5f3 MW |
590 | current = transaction_create(&this->public, request); |
591 | if (current == NULL) | |
592 | { | |
593 | this->logger->log(this->logger, ERROR, | |
594 | "no idea how to handle received message (%d), ignored", | |
595 | request->get_exchange_type(request)); | |
596 | return FAILED; | |
597 | } | |
ce461bbd MW |
598 | } |
599 | ||
3dd3c5f3 MW |
600 | /* send message. get_request() always gives a valid response */ |
601 | status = current->get_response(current, request, &response, &this->transaction_in_next); | |
602 | if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS) | |
ce461bbd | 603 | { |
147fe509 | 604 | this->logger->log(this->logger, ERROR, |
3dd3c5f3 MW |
605 | "response generation failed, discarding transaction"); |
606 | current->destroy(current); | |
b9d9f188 JH |
607 | return FAILED; |
608 | } | |
ce461bbd | 609 | |
3dd3c5f3 MW |
610 | charon->send_queue->add(charon->send_queue, packet); |
611 | this->time_outbound = time(NULL); | |
612 | /* act depending on transaction result */ | |
613 | switch (status) | |
614 | { | |
615 | case DESTROY_ME: | |
616 | /* transactions says we should destroy the IKE_SA, so do it */ | |
617 | current->destroy(current); | |
618 | return DESTROY_ME; | |
619 | default: | |
620 | /* store for retransmission, destroy old transaction */ | |
621 | this->transaction_in = current; | |
622 | if (last) | |
623 | { | |
624 | last->destroy(last); | |
625 | } | |
626 | return SUCCESS; | |
627 | } | |
628 | } | |
b9d9f188 | 629 | |
3dd3c5f3 MW |
630 | /** |
631 | * process an incoming response | |
632 | */ | |
633 | static status_t process_response(private_ike_sa_t *this, message_t *response) | |
634 | { | |
635 | transaction_t *current, *new = NULL; | |
ce461bbd | 636 | |
3dd3c5f3 MW |
637 | current = this->transaction_out; |
638 | /* check if message ID is that of our currently active transaction */ | |
639 | if (current == NULL || | |
640 | current->get_message_id(current) != | |
641 | response->get_message_id(response)) | |
ce461bbd | 642 | { |
3dd3c5f3 MW |
643 | this->logger->log(this->logger, ERROR, |
644 | "received response with message ID %d not requested, ignored"); | |
ce461bbd MW |
645 | return FAILED; |
646 | } | |
3dd3c5f3 MW |
647 | |
648 | switch (current->conclude(current, response, &new)) | |
b9d9f188 | 649 | { |
3dd3c5f3 MW |
650 | case DESTROY_ME: |
651 | /* state requested to destroy IKE_SA */ | |
652 | return DESTROY_ME; | |
653 | default: | |
654 | /* discard transaction, process next one */ | |
655 | break; | |
b9d9f188 | 656 | } |
3dd3c5f3 MW |
657 | /* transaction comleted, remove */ |
658 | current->destroy(current); | |
659 | this->transaction_out = NULL; | |
b9d9f188 | 660 | |
3dd3c5f3 MW |
661 | /* queue new transaction */ |
662 | return queue_transaction(this, new, TRUE); | |
663 | } | |
664 | ||
665 | /** | |
666 | * send a notify back to the sender | |
667 | */ | |
668 | static void send_notify_response(private_ike_sa_t *this, | |
669 | message_t *request, | |
670 | notify_type_t type) | |
671 | { | |
672 | notify_payload_t *notify; | |
673 | message_t *response; | |
674 | host_t *src, *dst; | |
675 | packet_t *packet; | |
676 | ||
677 | response = message_create(); | |
678 | dst = request->get_source(request); | |
679 | src = request->get_destination(request); | |
680 | response->set_source(response, src->clone(src)); | |
681 | response->set_destination(response, dst->clone(dst)); | |
682 | response->set_exchange_type(response, request->get_exchange_type(request)); | |
683 | response->set_request(response, FALSE); | |
684 | response->set_message_id(response, request->get_message_id(request)); | |
685 | response->set_ike_sa_id(response, this->ike_sa_id); | |
686 | notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type); | |
687 | response->add_payload(response, (payload_t *)notify); | |
688 | if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS) | |
b9d9f188 | 689 | { |
3dd3c5f3 MW |
690 | response->destroy(response); |
691 | return; | |
b9d9f188 | 692 | } |
3dd3c5f3 MW |
693 | charon->send_queue->add(charon->send_queue, packet); |
694 | this->time_outbound = time(NULL); | |
695 | response->destroy(response); | |
696 | return; | |
697 | } | |
698 | ||
699 | ||
700 | /** | |
701 | * Implementation of ike_sa_t.process_message. | |
702 | */ | |
703 | static status_t process_message(private_ike_sa_t *this, message_t *message) | |
704 | { | |
705 | status_t status; | |
706 | bool is_request; | |
ce461bbd | 707 | |
3dd3c5f3 | 708 | is_request = message->get_request(message); |
ce461bbd | 709 | |
3dd3c5f3 MW |
710 | status = message->parse_body(message, this->crypter_in, this->signer_in); |
711 | if (status != SUCCESS) | |
b9d9f188 | 712 | { |
3dd3c5f3 MW |
713 | switch (status) |
714 | { | |
715 | case NOT_SUPPORTED: | |
716 | this->logger->log(this->logger, ERROR, | |
717 | "ciritcal unknown payloads found"); | |
718 | if (is_request) | |
719 | { | |
720 | send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD); | |
721 | } | |
722 | break; | |
723 | case PARSE_ERROR: | |
724 | this->logger->log(this->logger, ERROR, | |
725 | "message parsing failed"); | |
726 | if (is_request) | |
727 | { | |
728 | send_notify_response(this, message, INVALID_SYNTAX); | |
729 | } | |
730 | break; | |
731 | case VERIFY_ERROR: | |
732 | this->logger->log(this->logger, ERROR, | |
733 | "message verification failed"); | |
734 | if (is_request) | |
735 | { | |
736 | send_notify_response(this, message, INVALID_SYNTAX); | |
737 | } | |
738 | break; | |
739 | case FAILED: | |
740 | this->logger->log(this->logger, ERROR, | |
741 | "integrity check failed"); | |
742 | /* ignored */ | |
743 | break; | |
744 | case INVALID_STATE: | |
745 | this->logger->log(this->logger, ERROR, | |
746 | "found encrypted message, but no keys available"); | |
747 | if (is_request) | |
748 | { | |
749 | send_notify_response(this, message, INVALID_SYNTAX); | |
750 | } | |
751 | default: | |
752 | break; | |
753 | } | |
754 | this->logger->log(this->logger, ERROR, | |
755 | "%s %s with message ID %d processing failed", | |
756 | mapping_find(exchange_type_m, message->get_exchange_type(message)), | |
757 | message->get_request(message) ? "request" : "response", | |
758 | message->get_message_id(message)); | |
b9d9f188 | 759 | } |
3dd3c5f3 | 760 | else |
ce461bbd | 761 | { |
3dd3c5f3 | 762 | /* check if message is trustworthy, and update connection information */ |
8dfbe71b | 763 | if (this->state == IKE_CREATED || |
3dd3c5f3 MW |
764 | message->get_exchange_type(message) != IKE_SA_INIT) |
765 | { | |
766 | update_hosts(this, message->get_destination(message), | |
767 | message->get_source(message)); | |
768 | this->time_inbound = time(NULL); | |
769 | } | |
770 | if (is_request) | |
771 | { | |
772 | status = process_request(this, message); | |
773 | } | |
774 | else | |
775 | { | |
776 | status = process_response(this, message); | |
777 | } | |
ce461bbd | 778 | } |
3dd3c5f3 | 779 | return status; |
aad398a7 JH |
780 | } |
781 | ||
782 | /** | |
3dd3c5f3 | 783 | * Implementation of ike_sa_t.initiate. |
aad398a7 | 784 | */ |
8dfbe71b MW |
785 | static status_t initiate(private_ike_sa_t *this, |
786 | connection_t *connection, policy_t *policy) | |
aad398a7 | 787 | { |
c0593835 MW |
788 | switch (this->state) |
789 | { | |
790 | case IKE_CREATED: | |
791 | { | |
792 | /* in state CREATED, we must do the ike_sa_init | |
793 | * and ike_auth transactions. Along with these, | |
794 | * a CHILD_SA with the supplied policy is set up. | |
795 | */ | |
796 | ike_sa_init_t *ike_sa_init; | |
797 | ||
45f76a7d MW |
798 | this->logger->log(this->logger, CONTROL, |
799 | "initiating IKE_SA"); | |
800 | ||
c0593835 | 801 | set_name(this, connection->get_name(connection)); |
45f76a7d | 802 | DESTROY_IF(this->my_host); |
c0593835 MW |
803 | this->my_host = connection->get_my_host(connection); |
804 | this->my_host = this->my_host->clone(this->my_host); | |
45f76a7d | 805 | DESTROY_IF(this->other_host); |
c0593835 MW |
806 | this->other_host = connection->get_other_host(connection); |
807 | this->other_host = this->other_host->clone(this->other_host); | |
808 | ||
809 | this->message_id_out = 1; | |
810 | ike_sa_init = ike_sa_init_create(&this->public); | |
811 | ike_sa_init->set_config(ike_sa_init, connection, policy); | |
812 | return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); | |
813 | } | |
814 | case IKE_DELETING: | |
815 | { | |
816 | /* if we are in DELETING, we deny set up of a policy. */ | |
45f76a7d MW |
817 | this->logger->log(this->logger, CONTROL, |
818 | "creating CHILD_SA discarded, as IKE_SA is deleting"); | |
c0593835 MW |
819 | policy->destroy(policy); |
820 | connection->destroy(connection); | |
821 | return FAILED; | |
822 | } | |
823 | case IKE_CONNECTING: | |
824 | case IKE_ESTABLISHED: | |
825 | { | |
826 | /* if we are ESTABLISHED or CONNECTING,we queue the | |
827 | * transaction to create the CHILD_SA. It gets processed | |
828 | * when the IKE_SA is ready to do so. We don't need the | |
829 | * connection, as the IKE_SA is already established/establishing. | |
830 | */ | |
831 | create_child_sa_t *create_child; | |
832 | ||
45f76a7d MW |
833 | this->logger->log(this->logger, CONTROL, |
834 | "initiating CHILD_SA"); | |
835 | ||
c0593835 MW |
836 | connection->destroy(connection); |
837 | create_child = create_child_sa_create(&this->public); | |
838 | create_child->set_policy(create_child, policy); | |
839 | return queue_transaction(this, (transaction_t*)create_child, FALSE); | |
840 | } | |
841 | } | |
842 | return FAILED; | |
aad398a7 JH |
843 | } |
844 | ||
8dfbe71b MW |
845 | /** |
846 | * Implementation of ike_sa_t.acquire. | |
847 | */ | |
848 | static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) | |
849 | { | |
45f76a7d MW |
850 | connection_t *connection; |
851 | policy_t *policy; | |
852 | iterator_t *iterator; | |
853 | child_sa_t *current, *child_sa = NULL; | |
854 | linked_list_t *my_ts, *other_ts; | |
855 | ||
856 | if (this->state == IKE_DELETING) | |
857 | { | |
858 | this->logger->log(this->logger, CONTROL, | |
859 | "acquiring CHILD_SA with reqid %d discarded, as IKE_SA is deleting", | |
860 | reqid); | |
861 | return FAILED; | |
862 | } | |
863 | ||
864 | ||
865 | /* find CHILD_SA */ | |
866 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); | |
867 | while (iterator->iterate(iterator, (void**)¤t)) | |
868 | { | |
869 | if (current->get_reqid(current) == reqid) | |
870 | { | |
871 | iterator->remove(iterator); | |
872 | child_sa = current; | |
873 | break; | |
874 | } | |
875 | } | |
876 | iterator->destroy(iterator); | |
877 | if (!child_sa) | |
878 | { | |
879 | this->logger->log(this->logger, ERROR, | |
880 | "CHILD_SA with reqid %d not found, unable to acquire", | |
881 | reqid); | |
882 | return FAILED; | |
883 | } | |
884 | my_ts = child_sa->get_my_traffic_selectors(child_sa); | |
885 | other_ts = child_sa->get_other_traffic_selectors(child_sa); | |
886 | ||
887 | policy = charon->policies->get_policy(charon->policies, | |
888 | this->my_id, this->other_id, | |
889 | my_ts, other_ts, | |
890 | this->my_host, this->other_host); | |
891 | child_sa->destroy(child_sa); | |
892 | if (policy == NULL) | |
893 | { | |
894 | this->logger->log(this->logger, ERROR, | |
895 | "no policy found to acquire CHILD_SA with reqid %d", | |
896 | reqid); | |
897 | return FAILED; | |
898 | } | |
899 | ||
8dfbe71b MW |
900 | switch (this->state) |
901 | { | |
902 | case IKE_CREATED: | |
45f76a7d MW |
903 | { |
904 | ike_sa_init_t *ike_sa_init; | |
905 | ||
906 | this->logger->log(this->logger, CONTROL, | |
907 | "acquiring CHILD_SA with reqid %d, IKE_SA setup needed", | |
908 | reqid); | |
909 | ||
910 | connection = charon->connections->get_connection_by_hosts( | |
911 | charon->connections, this->my_host, this->other_host); | |
912 | ||
913 | if (connection == NULL) | |
914 | { | |
915 | this->logger->log(this->logger, ERROR, | |
916 | "no connection found to acquire IKE_SA for CHILD_SA with reqid %d", | |
917 | reqid); | |
918 | policy->destroy(policy); | |
919 | return FAILED; | |
920 | } | |
921 | ||
922 | this->message_id_out = 1; | |
923 | ike_sa_init = ike_sa_init_create(&this->public); | |
924 | ike_sa_init->set_config(ike_sa_init, connection, policy); | |
925 | return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); | |
926 | } | |
927 | case IKE_CONNECTING: | |
928 | case IKE_ESTABLISHED: | |
929 | { | |
930 | create_child_sa_t *create_child; | |
931 | ||
932 | this->logger->log(this->logger, CONTROL, | |
933 | "acquiring CHILD_SA with reqid %d", | |
934 | reqid); | |
935 | ||
936 | create_child = create_child_sa_create(&this->public); | |
937 | create_child->set_policy(create_child, policy); | |
938 | return queue_transaction(this, (transaction_t*)create_child, FALSE); | |
939 | } | |
940 | default: | |
941 | break; | |
942 | } | |
943 | return FAILED; | |
944 | } | |
945 | ||
946 | /** | |
947 | * destroy a list of traffic selectors | |
948 | */ | |
949 | static void ts_list_destroy(linked_list_t *list) | |
950 | { | |
951 | traffic_selector_t *ts; | |
952 | while (list->remove_last(list, (void**)&ts) == SUCCESS) | |
953 | { | |
954 | ts->destroy(ts); | |
955 | } | |
956 | list->destroy(list); | |
957 | } | |
958 | ||
959 | /** | |
960 | * compare two lists of traffic selectors for equality | |
961 | */ | |
962 | static bool ts_list_equals(linked_list_t *l1, linked_list_t *l2) | |
963 | { | |
964 | bool equals = TRUE; | |
965 | iterator_t *i1, *i2; | |
966 | traffic_selector_t *t1, *t2; | |
967 | ||
968 | i1 = l1->create_iterator(l1, TRUE); | |
969 | i2 = l2->create_iterator(l2, TRUE); | |
970 | while (i1->iterate(i1, (void**)&t1) && i2->iterate(i2, (void**)&t2)) | |
971 | { | |
972 | if (!t1->equals(t1, t2)) | |
973 | { | |
974 | equals = FALSE; | |
8dfbe71b | 975 | break; |
45f76a7d MW |
976 | } |
977 | } | |
978 | /* check if one iterator is not at the end */ | |
979 | if (i1->has_next(i1) || i2->has_next(i2)) | |
980 | { | |
981 | equals = FALSE; | |
982 | } | |
983 | i1->destroy(i1); | |
984 | i2->destroy(i2); | |
985 | return equals; | |
986 | } | |
987 | ||
988 | /** | |
989 | * Implementation of ike_sa_t.route. | |
990 | */ | |
991 | static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t *policy) | |
992 | { | |
993 | child_sa_t *child_sa = NULL; | |
994 | iterator_t *iterator; | |
995 | linked_list_t *my_ts, *other_ts; | |
996 | status_t status; | |
997 | ||
998 | /* check if not already routed*/ | |
999 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); | |
1000 | while (iterator->iterate(iterator, (void**)&child_sa)) | |
1001 | { | |
1002 | linked_list_t *my_ts_conf, *other_ts_conf; | |
1003 | ||
1004 | my_ts = child_sa->get_my_traffic_selectors(child_sa); | |
1005 | other_ts = child_sa->get_other_traffic_selectors(child_sa); | |
1006 | ||
1007 | my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); | |
1008 | other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); | |
1009 | ||
1010 | if (ts_list_equals(my_ts, my_ts_conf) && | |
1011 | ts_list_equals(other_ts, other_ts_conf)) | |
1012 | { | |
1013 | ts_list_destroy(my_ts_conf); | |
1014 | ts_list_destroy(other_ts_conf); | |
1015 | iterator->destroy(iterator); | |
1016 | this->logger->log(this->logger, CONTROL, | |
1017 | "a CHILD_SA with such a policy already routed"); | |
1018 | ||
1019 | return FAILED; | |
1020 | } | |
1021 | ts_list_destroy(my_ts_conf); | |
1022 | ts_list_destroy(other_ts_conf); | |
1023 | } | |
1024 | iterator->destroy(iterator); | |
1025 | ||
1026 | switch (this->state) | |
1027 | { | |
1028 | case IKE_CREATED: | |
8dfbe71b | 1029 | case IKE_CONNECTING: |
45f76a7d MW |
1030 | /* we update IKE_SA information as good as possible, |
1031 | * this allows us to set up the SA later when an acquire comes in. */ | |
1032 | if (this->my_id->get_type(this->my_id) == ID_ANY) | |
1033 | { | |
1034 | this->my_id->destroy(this->my_id); | |
1035 | this->my_id = policy->get_my_id(policy); | |
1036 | this->my_id = this->my_id->clone(this->my_id); | |
1037 | } | |
1038 | if (this->other_id->get_type(this->other_id) == ID_ANY) | |
1039 | { | |
1040 | this->other_id->destroy(this->other_id); | |
1041 | this->other_id = policy->get_other_id(policy); | |
1042 | this->other_id = this->other_id->clone(this->other_id); | |
1043 | } | |
1044 | if (this->my_host->is_anyaddr(this->my_host)) | |
1045 | { | |
1046 | this->my_host->destroy(this->my_host); | |
1047 | this->my_host = connection->get_my_host(connection); | |
1048 | this->my_host = this->my_host->clone(this->my_host); | |
1049 | } | |
1050 | if (this->other_host->is_anyaddr(this->other_host)) | |
1051 | { | |
1052 | this->other_host->destroy(this->other_host); | |
1053 | this->other_host = connection->get_other_host(connection); | |
1054 | this->other_host = this->other_host->clone(this->other_host); | |
1055 | } | |
1056 | set_name(this, connection->get_name(connection)); | |
1057 | break; | |
8dfbe71b | 1058 | case IKE_ESTABLISHED: |
45f76a7d | 1059 | /* nothing to do */ |
8dfbe71b MW |
1060 | break; |
1061 | case IKE_DELETING: | |
1062 | /* deny */ | |
45f76a7d | 1063 | return FAILED; |
8dfbe71b | 1064 | } |
45f76a7d MW |
1065 | |
1066 | child_sa = child_sa_create(0, this->my_host, this->other_host, 0, 0, FALSE); | |
1067 | my_ts = policy->get_my_traffic_selectors(policy, this->my_host); | |
1068 | other_ts = policy->get_other_traffic_selectors(policy, this->other_host); | |
1069 | status = child_sa->add_policies(child_sa, my_ts, other_ts); | |
1070 | ts_list_destroy(my_ts); | |
1071 | ts_list_destroy(other_ts); | |
1072 | this->child_sas->insert_last(this->child_sas, child_sa); | |
1073 | ||
1074 | return status; | |
8dfbe71b MW |
1075 | } |
1076 | ||
1077 | /** | |
45f76a7d | 1078 | * Implementation of ike_sa_t.unroute. |
8dfbe71b | 1079 | */ |
45f76a7d | 1080 | static status_t unroute(private_ike_sa_t *this, policy_t *policy) |
8dfbe71b | 1081 | { |
45f76a7d MW |
1082 | iterator_t *iterator; |
1083 | child_sa_t *child_sa = NULL; | |
1084 | linked_list_t *my_ts, *other_ts, *my_ts_conf, *other_ts_conf; | |
1085 | ||
1086 | /* find CHILD_SA in ROUTED state */ | |
1087 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); | |
1088 | while (iterator->iterate(iterator, (void**)&child_sa)) | |
1089 | { | |
1090 | if (child_sa->get_state(child_sa) == CHILD_ROUTED) | |
1091 | { | |
1092 | my_ts = child_sa->get_my_traffic_selectors(child_sa); | |
1093 | other_ts = child_sa->get_other_traffic_selectors(child_sa); | |
1094 | ||
1095 | my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); | |
1096 | other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); | |
1097 | ||
1098 | if (ts_list_equals(my_ts, my_ts_conf) && | |
1099 | ts_list_equals(other_ts, other_ts_conf)) | |
1100 | { | |
1101 | iterator->remove(iterator); | |
1102 | child_sa->destroy(child_sa); | |
1103 | ts_list_destroy(my_ts_conf); | |
1104 | ts_list_destroy(other_ts_conf); | |
1105 | break; | |
1106 | } | |
1107 | ts_list_destroy(my_ts_conf); | |
1108 | ts_list_destroy(other_ts_conf); | |
1109 | } | |
1110 | } | |
1111 | iterator->destroy(iterator); | |
1112 | /* if we are not established, and we have no more routed childs, remove whole SA */ | |
1113 | if (this->state == IKE_CREATED && | |
1114 | this->child_sas->get_count(this->child_sas) == 0) | |
1115 | { | |
1116 | return DESTROY_ME; | |
1117 | } | |
1118 | return SUCCESS; | |
8dfbe71b MW |
1119 | } |
1120 | ||
f890d8fe | 1121 | /** |
3dd3c5f3 | 1122 | * Implementation of ike_sa_t.send_dpd |
f890d8fe | 1123 | */ |
3dd3c5f3 | 1124 | static status_t send_dpd(private_ike_sa_t *this) |
f890d8fe | 1125 | { |
3dd3c5f3 MW |
1126 | send_dpd_job_t *job; |
1127 | time_t diff, interval; | |
1128 | status_t status = SUCCESS; | |
1129 | ||
1130 | interval = charon->configuration->get_dpd_interval(charon->configuration); | |
1131 | ||
1132 | if (this->transaction_out) | |
1133 | { | |
1134 | /* there is a transaction in progress. Come back later */ | |
1135 | diff = 0; | |
1136 | } | |
1137 | else | |
1138 | { | |
1139 | /* check if there was any inbound traffic */ | |
1140 | time_t last_in, now; | |
1141 | last_in = get_time_inbound(this); | |
1142 | now = time(NULL); | |
1143 | diff = now - last_in; | |
1144 | if (diff >= interval) | |
1145 | { | |
1146 | /* to long ago, initiate dead peer detection */ | |
1147 | dead_peer_detection_t *dpd; | |
1148 | this->logger->log(this->logger, CONTROL, "sending DPD request"); | |
bcb95ced | 1149 | dpd = dead_peer_detection_create(&this->public); |
3dd3c5f3 MW |
1150 | status = queue_transaction(this, (transaction_t*)dpd, FALSE); |
1151 | diff = 0; | |
1152 | } | |
1153 | } | |
1154 | /* recheck in "interval" seconds */ | |
1155 | job = send_dpd_job_create(this->ike_sa_id); | |
1156 | charon->event_queue->add_relative(charon->event_queue, (job_t*)job, | |
1157 | (interval - diff) * 1000); | |
1158 | return SUCCESS; | |
f890d8fe JH |
1159 | } |
1160 | ||
1161 | /** | |
3dd3c5f3 | 1162 | * Implementation of ike_sa_t.send_keepalive |
f890d8fe | 1163 | */ |
3dd3c5f3 | 1164 | static void send_keepalive(private_ike_sa_t *this) |
f890d8fe | 1165 | { |
3dd3c5f3 MW |
1166 | send_keepalive_job_t *job; |
1167 | time_t last_out, now, diff, interval; | |
1168 | ||
1169 | last_out = get_time_outbound(this); | |
1170 | now = time(NULL); | |
1171 | ||
1172 | diff = now - last_out; | |
1173 | interval = charon->configuration->get_keepalive_interval(charon->configuration); | |
1174 | ||
1175 | if (diff >= interval) | |
1176 | { | |
3dd3c5f3 MW |
1177 | packet_t *packet; |
1178 | chunk_t data; | |
1179 | ||
1180 | packet = packet_create(); | |
8dfbe71b MW |
1181 | packet->set_source(packet, this->my_host->clone(this->my_host)); |
1182 | packet->set_destination(packet, this->other_host->clone(this->other_host)); | |
3dd3c5f3 MW |
1183 | data.ptr = malloc(1); |
1184 | data.ptr[0] = 0xFF; | |
1185 | data.len = 1; | |
1186 | packet->set_data(packet, data); | |
1187 | charon->send_queue->add(charon->send_queue, packet); | |
1188 | this->logger->log(this->logger, CONTROL, "sending keep alive"); | |
1189 | diff = 0; | |
1190 | } | |
1191 | job = send_keepalive_job_create(this->ike_sa_id); | |
1192 | charon->event_queue->add_relative(charon->event_queue, (job_t*)job, | |
1193 | (interval - diff) * 1000); | |
f890d8fe JH |
1194 | } |
1195 | ||
ccf783d2 | 1196 | /** |
3dd3c5f3 | 1197 | * Implementation of ike_sa_t.get_state. |
ccf783d2 | 1198 | */ |
3dd3c5f3 | 1199 | static ike_sa_state_t get_state(private_ike_sa_t *this) |
ccf783d2 | 1200 | { |
3dd3c5f3 | 1201 | return this->state; |
ccf783d2 MW |
1202 | } |
1203 | ||
1204 | /** | |
3dd3c5f3 | 1205 | * Implementation of ike_sa_t.set_state. |
ccf783d2 | 1206 | */ |
3dd3c5f3 | 1207 | static void set_state(private_ike_sa_t *this, ike_sa_state_t state) |
ccf783d2 | 1208 | { |
3dd3c5f3 MW |
1209 | this->logger->log(this->logger, CONTROL, "state change: %s => %s", |
1210 | mapping_find(ike_sa_state_m, this->state), | |
1211 | mapping_find(ike_sa_state_m, state)); | |
bcb95ced | 1212 | if (state == IKE_ESTABLISHED) |
3dd3c5f3 | 1213 | { |
3dd3c5f3 | 1214 | this->logger->log(this->logger, AUDIT, "IKE_SA established: %s[%s]...%s[%s]", |
8dfbe71b MW |
1215 | this->my_host->get_string(this->my_host), |
1216 | this->my_id->get_string(this->my_id), | |
1217 | this->other_host->get_string(this->other_host), | |
1218 | this->other_id->get_string(this->other_id)); | |
3dd3c5f3 MW |
1219 | |
1220 | send_dpd(this); | |
1221 | } | |
1222 | this->state = state; | |
ccf783d2 MW |
1223 | } |
1224 | ||
2f89902d | 1225 | /** |
8dfbe71b | 1226 | * Implementation of protected_ike_sa_t.get_prf. |
2f89902d | 1227 | */ |
8dfbe71b | 1228 | static prf_t *get_prf(private_ike_sa_t *this) |
2f89902d | 1229 | { |
8dfbe71b | 1230 | return this->prf; |
2f89902d MW |
1231 | } |
1232 | ||
aad398a7 | 1233 | /** |
8dfbe71b | 1234 | * Implementation of protected_ike_sa_t.get_prf. |
aad398a7 | 1235 | */ |
8dfbe71b | 1236 | static prf_t *get_child_prf(private_ike_sa_t *this) |
aad398a7 | 1237 | { |
8dfbe71b | 1238 | return this->child_prf; |
aad398a7 JH |
1239 | } |
1240 | ||
aad398a7 | 1241 | /** |
8dfbe71b | 1242 | * Implementation of protected_ike_sa_t.get_prf_auth_i. |
aad398a7 | 1243 | */ |
8dfbe71b | 1244 | static prf_t *get_prf_auth_i(private_ike_sa_t *this) |
aad398a7 | 1245 | { |
8dfbe71b | 1246 | return this->prf_auth_i; |
aad398a7 JH |
1247 | } |
1248 | ||
283dbcc5 | 1249 | /** |
8dfbe71b | 1250 | * Implementation of protected_ike_sa_t.get_prf_auth_r. |
283dbcc5 | 1251 | */ |
8dfbe71b | 1252 | static prf_t *get_prf_auth_r(private_ike_sa_t *this) |
283dbcc5 | 1253 | { |
8dfbe71b | 1254 | return this->prf_auth_r; |
283dbcc5 MW |
1255 | } |
1256 | ||
ae3012a0 | 1257 | /** |
8dfbe71b | 1258 | * Implementation of ike_sa_t.get_id. |
0df63d6b | 1259 | */ |
8dfbe71b | 1260 | static ike_sa_id_t* get_id(private_ike_sa_t *this) |
0df63d6b | 1261 | { |
8dfbe71b | 1262 | return this->ike_sa_id; |
0df63d6b JH |
1263 | } |
1264 | ||
0fdc3c7f | 1265 | /** |
8dfbe71b | 1266 | * Implementation of ike_sa_t.get_my_id. |
0fdc3c7f | 1267 | */ |
8dfbe71b | 1268 | static identification_t* get_my_id(private_ike_sa_t *this) |
0fdc3c7f | 1269 | { |
8dfbe71b | 1270 | return this->my_id; |
8d68033e JH |
1271 | } |
1272 | ||
1273 | /** | |
8dfbe71b | 1274 | * Implementation of ike_sa_t.set_my_id. |
8d68033e | 1275 | */ |
8dfbe71b | 1276 | static void set_my_id(private_ike_sa_t *this, identification_t *me) |
8d68033e | 1277 | { |
c0593835 | 1278 | DESTROY_IF(this->my_id); |
8dfbe71b | 1279 | this->my_id = me; |
0fdc3c7f JH |
1280 | } |
1281 | ||
30b5b412 | 1282 | /** |
8dfbe71b | 1283 | * Implementation of ike_sa_t.get_other_id. |
30b5b412 | 1284 | */ |
8dfbe71b | 1285 | static identification_t* get_other_id(private_ike_sa_t *this) |
30b5b412 | 1286 | { |
8dfbe71b | 1287 | return this->other_id; |
3dd3c5f3 | 1288 | } |
8dfbe71b | 1289 | |
3dd3c5f3 | 1290 | /** |
8dfbe71b | 1291 | * Implementation of ike_sa_t.set_other_id. |
3dd3c5f3 | 1292 | */ |
8dfbe71b | 1293 | static void set_other_id(private_ike_sa_t *this, identification_t *other) |
3dd3c5f3 | 1294 | { |
c0593835 | 1295 | DESTROY_IF(this->other_id); |
8dfbe71b | 1296 | this->other_id = other; |
30b5b412 MW |
1297 | } |
1298 | ||
8d77edde | 1299 | /** |
3dd3c5f3 | 1300 | * Implementation of protected_ike_sa_t.build_transforms. |
8d77edde | 1301 | */ |
3dd3c5f3 MW |
1302 | static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, |
1303 | diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, | |
1304 | bool initiator) | |
8d77edde | 1305 | { |
3dd3c5f3 MW |
1306 | chunk_t nonces, nonces_spis, skeyseed, key, secret; |
1307 | u_int64_t spi_i, spi_r; | |
1308 | prf_plus_t *prf_plus; | |
1309 | algorithm_t *algo; | |
1310 | size_t key_size; | |
1311 | crypter_t *crypter_i, *crypter_r; | |
1312 | signer_t *signer_i, *signer_r; | |
8d77edde | 1313 | |
3dd3c5f3 MW |
1314 | /* Build the PRF+ instance for deriving keys */ |
1315 | if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo)) | |
8d77edde | 1316 | { |
3dd3c5f3 MW |
1317 | this->logger->log(this->logger, ERROR, "no PSEUDO_RANDOM_FUNCTION selected!"); |
1318 | return FAILED; | |
8d77edde | 1319 | } |
3dd3c5f3 MW |
1320 | this->prf = prf_create(algo->algorithm); |
1321 | if (this->prf == NULL) | |
8d77edde | 1322 | { |
3dd3c5f3 MW |
1323 | this->logger->log(this->logger, ERROR, "PSEUDO_RANDOM_FUNCTION %s not supported!", |
1324 | mapping_find(pseudo_random_function_m, algo->algorithm)); | |
1325 | return FAILED; | |
8d77edde MW |
1326 | } |
1327 | ||
3dd3c5f3 MW |
1328 | /* nonces = nonce_i | nonce_r */ |
1329 | nonces = chunk_alloc(nonce_i.len + nonce_r.len); | |
1330 | memcpy(nonces.ptr, nonce_i.ptr, nonce_i.len); | |
1331 | memcpy(nonces.ptr + nonce_i.len, nonce_r.ptr, nonce_r.len); | |
8d77edde | 1332 | |
3dd3c5f3 MW |
1333 | /* prf_seed = nonce_i | nonce_r | spi_i | spi_r */ |
1334 | nonces_spis = chunk_alloc(nonces.len + 16); | |
1335 | memcpy(nonces_spis.ptr, nonces.ptr, nonces.len); | |
1336 | spi_i = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); | |
1337 | spi_r = this->ike_sa_id->get_responder_spi(this->ike_sa_id); | |
1338 | memcpy(nonces_spis.ptr + nonces.len, &spi_i, 8); | |
1339 | memcpy(nonces_spis.ptr + nonces.len + 8, &spi_r, 8); | |
1340 | ||
1341 | /* SKEYSEED = prf(Ni | Nr, g^ir) */ | |
1342 | dh->get_shared_secret(dh, &secret); | |
1343 | this->logger->log_chunk(this->logger, PRIVATE, "shared Diffie Hellman secret", secret); | |
1344 | this->prf->set_key(this->prf, nonces); | |
1345 | this->prf->allocate_bytes(this->prf, secret, &skeyseed); | |
1346 | this->logger->log_chunk(this->logger, PRIVATE|LEVEL1, "SKEYSEED", skeyseed); | |
1347 | chunk_free(&secret); | |
8d77edde | 1348 | |
3dd3c5f3 MW |
1349 | /* prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr ) |
1350 | * = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr | |
1351 | */ | |
1352 | this->prf->set_key(this->prf, skeyseed); | |
1353 | prf_plus = prf_plus_create(this->prf, nonces_spis); | |
8d77edde | 1354 | |
3dd3c5f3 MW |
1355 | /* clean up unused stuff */ |
1356 | chunk_free(&nonces); | |
1357 | chunk_free(&nonces_spis); | |
1358 | chunk_free(&skeyseed); | |
1359 | ||
1360 | /* SK_d used for prf+ to derive keys for child SAs */ | |
1361 | this->child_prf = prf_create(algo->algorithm); | |
1362 | key_size = this->child_prf->get_key_size(this->child_prf); | |
1363 | prf_plus->allocate_bytes(prf_plus, key_size, &key); | |
1364 | this->logger->log_chunk(this->logger, PRIVATE, "Sk_d secret", key); | |
1365 | this->child_prf->set_key(this->child_prf, key); | |
1366 | chunk_free(&key); | |
1367 | ||
1368 | /* SK_ai/SK_ar used for integrity protection */ | |
1369 | if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &algo)) | |
8d77edde | 1370 | { |
3dd3c5f3 | 1371 | this->logger->log(this->logger, ERROR, "no INTEGRITY_ALGORITHM selected?!"); |
8d77edde MW |
1372 | return FAILED; |
1373 | } | |
1374 | ||
3dd3c5f3 MW |
1375 | signer_i = signer_create(algo->algorithm); |
1376 | signer_r = signer_create(algo->algorithm); | |
1377 | if (signer_i == NULL || signer_r == NULL) | |
1396815a | 1378 | { |
3dd3c5f3 MW |
1379 | this->logger->log(this->logger, ERROR, "INTEGRITY_ALGORITHM %s not supported!", |
1380 | mapping_find(integrity_algorithm_m,algo->algorithm)); | |
1381 | return FAILED; | |
1396815a | 1382 | } |
3dd3c5f3 MW |
1383 | key_size = signer_i->get_key_size(signer_i); |
1384 | ||
1385 | prf_plus->allocate_bytes(prf_plus, key_size, &key); | |
1386 | this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ai secret", key); | |
1387 | signer_i->set_key(signer_i, key); | |
1388 | chunk_free(&key); | |
1396815a | 1389 | |
3dd3c5f3 MW |
1390 | prf_plus->allocate_bytes(prf_plus, key_size, &key); |
1391 | this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ar secret", key); | |
1392 | signer_r->set_key(signer_r, key); | |
1393 | chunk_free(&key); | |
1396815a | 1394 | |
3dd3c5f3 | 1395 | if (initiator) |
1396815a | 1396 | { |
0d379627 MW |
1397 | this->signer_in = signer_r; |
1398 | this->signer_out = signer_i; | |
1396815a MW |
1399 | } |
1400 | else | |
1401 | { | |
0d379627 MW |
1402 | this->signer_in = signer_i; |
1403 | this->signer_out = signer_r; | |
1396815a | 1404 | } |
3dd3c5f3 MW |
1405 | |
1406 | /* SK_ei/SK_er used for encryption */ | |
1407 | if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &algo)) | |
1396815a | 1408 | { |
3dd3c5f3 MW |
1409 | this->logger->log(this->logger, ERROR, "no ENCRYPTION_ALGORITHM selected!"); |
1410 | return FAILED; | |
1411 | } | |
1412 | crypter_i = crypter_create(algo->algorithm, algo->key_size / 8); | |
1413 | crypter_r = crypter_create(algo->algorithm, algo->key_size / 8); | |
1414 | if (crypter_i == NULL || crypter_r == NULL) | |
1415 | { | |
1416 | this->logger->log(this->logger, ERROR, | |
1417 | "ENCRYPTION_ALGORITHM %s (key size %d) not supported!", | |
1418 | mapping_find(encryption_algorithm_m, algo->algorithm), | |
1419 | algo->key_size); | |
1420 | return FAILED; | |
1396815a | 1421 | } |
3dd3c5f3 | 1422 | key_size = crypter_i->get_key_size(crypter_i); |
1396815a | 1423 | |
3dd3c5f3 MW |
1424 | prf_plus->allocate_bytes(prf_plus, key_size, &key); |
1425 | this->logger->log_chunk(this->logger, PRIVATE, "Sk_ei secret", key); | |
1426 | crypter_i->set_key(crypter_i, key); | |
1427 | chunk_free(&key); | |
1428 | ||
1429 | prf_plus->allocate_bytes(prf_plus, key_size, &key); | |
1430 | this->logger->log_chunk(this->logger, PRIVATE, "Sk_er secret", key); | |
1431 | crypter_r->set_key(crypter_r, key); | |
1432 | chunk_free(&key); | |
1433 | ||
1434 | if (initiator) | |
1396815a | 1435 | { |
0d379627 MW |
1436 | this->crypter_in = crypter_r; |
1437 | this->crypter_out = crypter_i; | |
1396815a | 1438 | } |
3dd3c5f3 MW |
1439 | else |
1440 | { | |
0d379627 MW |
1441 | this->crypter_in = crypter_i; |
1442 | this->crypter_out = crypter_r; | |
3dd3c5f3 MW |
1443 | } |
1444 | ||
1445 | /* SK_pi/SK_pr used for authentication */ | |
1446 | proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo); | |
1447 | this->prf_auth_i = prf_create(algo->algorithm); | |
1448 | this->prf_auth_r = prf_create(algo->algorithm); | |
1449 | ||
1450 | key_size = this->prf_auth_i->get_key_size(this->prf_auth_i); | |
1451 | prf_plus->allocate_bytes(prf_plus, key_size, &key); | |
1452 | this->logger->log_chunk(this->logger, PRIVATE, "Sk_pi secret", key); | |
1453 | this->prf_auth_i->set_key(this->prf_auth_i, key); | |
1454 | chunk_free(&key); | |
1455 | ||
1456 | prf_plus->allocate_bytes(prf_plus, key_size, &key); | |
1457 | this->logger->log_chunk(this->logger, PRIVATE, "Sk_pr secret", key); | |
1458 | this->prf_auth_r->set_key(this->prf_auth_r, key); | |
1459 | chunk_free(&key); | |
1460 | ||
1461 | /* all done, prf_plus not needed anymore */ | |
1462 | prf_plus->destroy(prf_plus); | |
1463 | ||
1464 | return SUCCESS; | |
1396815a MW |
1465 | } |
1466 | ||
32b6500f | 1467 | /** |
3dd3c5f3 | 1468 | * Implementation of protected_ike_sa_t.add_child_sa. |
32b6500f | 1469 | */ |
3dd3c5f3 | 1470 | static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) |
32b6500f | 1471 | { |
3dd3c5f3 | 1472 | this->child_sas->insert_last(this->child_sas, child_sa); |
32b6500f MW |
1473 | } |
1474 | ||
45f76a7d MW |
1475 | /** |
1476 | * Implementation of protected_ike_sa_t.has_child_sa. | |
1477 | */ | |
1478 | static bool has_child_sa(private_ike_sa_t *this, u_int32_t reqid) | |
1479 | { | |
1480 | iterator_t *iterator; | |
1481 | child_sa_t *current; | |
1482 | bool found = FALSE; | |
1483 | ||
1484 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); | |
1485 | while (iterator->iterate(iterator, (void**)¤t)) | |
1486 | { | |
1487 | if (current->get_reqid(current) == reqid) | |
1488 | { | |
1489 | found = TRUE; | |
1490 | break; | |
1491 | } | |
1492 | } | |
1493 | iterator->destroy(iterator); | |
1494 | return found; | |
1495 | } | |
1496 | ||
695723d4 MW |
1497 | /** |
1498 | * Implementation of protected_ike_sa_t.get_child_sa. | |
1499 | */ | |
698d7749 MW |
1500 | static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, |
1501 | u_int32_t spi, bool inbound) | |
695723d4 MW |
1502 | { |
1503 | iterator_t *iterator; | |
1504 | child_sa_t *current, *found = NULL; | |
1505 | ||
45f76a7d | 1506 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); |
695723d4 MW |
1507 | while (iterator->has_next(iterator)) |
1508 | { | |
1509 | iterator->current(iterator, (void**)¤t); | |
698d7749 | 1510 | if (current->get_spi(current, inbound) == spi && |
45f76a7d | 1511 | current->get_protocol(current) == protocol) |
695723d4 MW |
1512 | { |
1513 | found = current; | |
1514 | } | |
1515 | } | |
1516 | iterator->destroy(iterator); | |
1517 | return found; | |
8d77edde MW |
1518 | } |
1519 | ||
1520 | /** | |
1521 | * Implementation of ike_sa_t.rekey_child_sa. | |
1522 | */ | |
698d7749 | 1523 | static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) |
8d77edde | 1524 | { |
698d7749 | 1525 | create_child_sa_t *rekey; |
8d77edde | 1526 | child_sa_t *child_sa; |
8d77edde | 1527 | |
698d7749 MW |
1528 | child_sa = get_child_sa(this, protocol, spi, TRUE); |
1529 | if (child_sa == NULL) | |
8d77edde | 1530 | { |
698d7749 | 1531 | return NOT_FOUND; |
8d77edde MW |
1532 | } |
1533 | ||
bcb95ced | 1534 | rekey = create_child_sa_create(&this->public); |
698d7749 MW |
1535 | rekey->rekeys_child(rekey, child_sa); |
1536 | return queue_transaction(this, (transaction_t*)rekey, FALSE); | |
1537 | } | |
1538 | ||
1539 | /** | |
1540 | * Implementation of ike_sa_t.delete_child_sa. | |
1541 | */ | |
1542 | static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) | |
1543 | { | |
1544 | delete_child_sa_t *del; | |
1545 | child_sa_t *child_sa; | |
1546 | ||
1547 | child_sa = get_child_sa(this, protocol, spi, TRUE); | |
8d77edde MW |
1548 | if (child_sa == NULL) |
1549 | { | |
698d7749 | 1550 | return NOT_FOUND; |
8d77edde MW |
1551 | } |
1552 | ||
bcb95ced | 1553 | del = delete_child_sa_create(&this->public); |
698d7749 MW |
1554 | del->set_child_sa(del, child_sa); |
1555 | return queue_transaction(this, (transaction_t*)del, FALSE); | |
1556 | } | |
1557 | ||
1558 | /** | |
1559 | * Implementation of protected_ike_sa_t.destroy_child_sa. | |
1560 | */ | |
1561 | static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) | |
1562 | { | |
1563 | iterator_t *iterator; | |
1564 | child_sa_t *child_sa; | |
1565 | status_t status = NOT_FOUND; | |
8d77edde | 1566 | |
698d7749 MW |
1567 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); |
1568 | while (iterator->iterate(iterator, (void**)&child_sa)) | |
1569 | { | |
1570 | if (child_sa->get_protocol(child_sa) == protocol && | |
1571 | child_sa->get_spi(child_sa, TRUE) == spi) | |
1572 | { | |
1573 | child_sa->destroy(child_sa); | |
1574 | iterator->remove(iterator); | |
1575 | status = SUCCESS; | |
1576 | break; | |
1577 | } | |
8d77edde | 1578 | } |
698d7749 MW |
1579 | iterator->destroy(iterator); |
1580 | return status; | |
8d77edde MW |
1581 | } |
1582 | ||
aad398a7 | 1583 | |
b92eef28 MW |
1584 | /** |
1585 | * Implementation of protected_ike_sa_t.log_status. | |
1586 | */ | |
e168ee17 | 1587 | static void log_status(private_ike_sa_t *this, logger_t *logger, char *name) |
b92eef28 MW |
1588 | { |
1589 | iterator_t *iterator; | |
1590 | child_sa_t *child_sa; | |
8dfbe71b | 1591 | char *my_host, *other_host, *my_id, *other_id; |
b92eef28 | 1592 | |
8dfbe71b | 1593 | if (name == NULL || streq(name, this->name)) |
e168ee17 | 1594 | { |
8dfbe71b | 1595 | if (logger == NULL) |
e168ee17 | 1596 | { |
8dfbe71b | 1597 | logger = this->logger; |
e168ee17 | 1598 | } |
8dfbe71b MW |
1599 | |
1600 | my_host = this->my_host ? | |
1601 | this->my_host->get_string(this->my_host) : "(unknown)"; | |
1602 | other_host = this->other_host ? | |
1603 | this->other_host->get_string(this->other_host) : "(unknown)"; | |
1604 | my_id = this->my_id ? | |
1605 | this->my_id->get_string(this->my_id) : "(unknown)"; | |
1606 | other_id = this->other_id ? | |
1607 | this->other_id->get_string(this->other_id) : "(unknown)"; | |
1608 | ||
1609 | logger->log(logger, CONTROL|LEVEL1, | |
1610 | " \"%s\": IKE_SA in state %s, SPIs: 0x%.16llx 0x%.16llx", | |
1611 | this->name, | |
1612 | mapping_find(ike_sa_state_m, this->state), | |
1613 | this->ike_sa_id->get_initiator_spi(this->ike_sa_id), | |
1614 | this->ike_sa_id->get_responder_spi(this->ike_sa_id)); | |
1615 | logger->log(logger, CONTROL, " \"%s\": %s[%s]...%s[%s]", | |
1616 | this->name, my_host, my_id, other_host, other_id); | |
1617 | ||
1618 | iterator = this->child_sas->create_iterator(this->child_sas, TRUE); | |
1619 | while (iterator->has_next(iterator)) | |
1620 | { | |
1621 | iterator->current(iterator, (void**)&child_sa); | |
1622 | child_sa->log_status(child_sa, logger, this->name); | |
1623 | } | |
1624 | iterator->destroy(iterator); | |
e168ee17 | 1625 | } |
b92eef28 MW |
1626 | } |
1627 | ||
4a5bba25 MW |
1628 | /** |
1629 | * Implementation of public_ike_sa_t.delete. | |
1630 | */ | |
1631 | static status_t delete_(private_ike_sa_t *this) | |
1632 | { | |
45f76a7d | 1633 | switch (this->state) |
325e4977 | 1634 | { |
45f76a7d MW |
1635 | case IKE_CONNECTING: |
1636 | case IKE_ESTABLISHED: | |
1637 | { | |
1638 | delete_ike_sa_t *delete_ike_sa; | |
1639 | delete_ike_sa = delete_ike_sa_create(&this->public); | |
1640 | if (this->transaction_out) | |
1641 | { | |
1642 | /* already a transaction in progress. As this may hang | |
1643 | * around a while, we don't inform the other peer. */ | |
1644 | return DESTROY_ME; | |
1645 | } | |
1646 | return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE); | |
1647 | } | |
1648 | case IKE_CREATED: | |
1649 | case IKE_DELETING: | |
1650 | default: | |
1651 | { | |
1652 | return DESTROY_ME; | |
1653 | } | |
325e4977 | 1654 | } |
1396815a MW |
1655 | } |
1656 | ||
bcb95ced MW |
1657 | /** |
1658 | * Implementation of ike_sa_t.get_next_message_id. | |
1659 | */ | |
1660 | static u_int32_t get_next_message_id (private_ike_sa_t *this) | |
1661 | { | |
1662 | return this->message_id_out++; | |
1663 | } | |
1664 | ||
1396815a | 1665 | /** |
3dd3c5f3 | 1666 | * Implementation of ike_sa_t.is_natt_enabled. |
1396815a | 1667 | */ |
3dd3c5f3 | 1668 | static bool is_natt_enabled (private_ike_sa_t *this) |
1396815a MW |
1669 | { |
1670 | return this->nat_here || this->nat_there; | |
1671 | } | |
1672 | ||
1673 | /** | |
3dd3c5f3 | 1674 | * Implementation of protected_ike_sa_t.enable_natt. |
1396815a | 1675 | */ |
3dd3c5f3 | 1676 | static void enable_natt (private_ike_sa_t *this, bool local) |
1396815a | 1677 | { |
3dd3c5f3 | 1678 | if (local) |
2f89902d | 1679 | { |
3dd3c5f3 MW |
1680 | this->logger->log(this->logger, CONTROL, |
1681 | "local host is behind NAT, using NAT-T, scheduled keep alives"); | |
1682 | this->nat_here = TRUE; | |
1683 | send_keepalive(this); | |
1684 | } | |
1685 | else | |
1686 | { | |
1687 | this->logger->log(this->logger, CONTROL, | |
1688 | "remote host is behind NAT, using NAT-T"); | |
1689 | this->nat_there = TRUE; | |
2f89902d | 1690 | } |
1396815a MW |
1691 | } |
1692 | ||
60a6167a | 1693 | /** |
ae3012a0 | 1694 | * Implementation of protected_ike_sa_t.destroy. |
60a6167a | 1695 | */ |
4a5bba25 | 1696 | static void destroy(private_ike_sa_t *this) |
60a6167a | 1697 | { |
30b5b412 | 1698 | child_sa_t *child_sa; |
3dd3c5f3 | 1699 | transaction_t *transaction; |
8dfbe71b | 1700 | char *my_host, *other_host, *my_id, *other_id; |
30b5b412 | 1701 | |
971218c3 | 1702 | this->logger->log(this->logger, CONTROL|LEVEL2, "going to destroy IKE SA %llu:%llu, role %s", |
94b0f906 JH |
1703 | this->ike_sa_id->get_initiator_spi(this->ike_sa_id), |
1704 | this->ike_sa_id->get_responder_spi(this->ike_sa_id), | |
1705 | this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); | |
4a5bba25 | 1706 | |
bcb95ced | 1707 | if (this->state == IKE_ESTABLISHED) |
4a5bba25 | 1708 | { |
3dd3c5f3 MW |
1709 | this->logger->log(this->logger, ERROR, |
1710 | "destroying an established IKE SA without knowledge from remote peer!"); | |
4a5bba25 | 1711 | } |
8323a9c1 | 1712 | |
30b5b412 | 1713 | while (this->child_sas->remove_last(this->child_sas, (void**)&child_sa) == SUCCESS) |
60a6167a | 1714 | { |
30b5b412 | 1715 | child_sa->destroy(child_sa); |
60a6167a | 1716 | } |
7ba38761 | 1717 | this->child_sas->destroy(this->child_sas); |
87a217f9 | 1718 | |
3dd3c5f3 MW |
1719 | while (this->transaction_queue->remove_last(this->transaction_queue, (void**)&transaction) == SUCCESS) |
1720 | { | |
1721 | transaction->destroy(transaction); | |
1722 | } | |
1723 | this->transaction_queue->destroy(this->transaction_queue); | |
8dfbe71b MW |
1724 | |
1725 | DESTROY_IF(this->transaction_in); | |
1726 | DESTROY_IF(this->transaction_in_next); | |
1727 | DESTROY_IF(this->transaction_out); | |
1728 | DESTROY_IF(this->crypter_in); | |
1729 | DESTROY_IF(this->crypter_out); | |
1730 | DESTROY_IF(this->signer_in); | |
1731 | DESTROY_IF(this->signer_out); | |
1732 | DESTROY_IF(this->prf); | |
1733 | DESTROY_IF(this->child_prf); | |
1734 | DESTROY_IF(this->prf_auth_i); | |
1735 | DESTROY_IF(this->prf_auth_r); | |
1736 | ||
1737 | my_host = this->my_host ? | |
1738 | this->my_host->get_string(this->my_host) : "(unknown)"; | |
1739 | other_host = this->other_host ? | |
1740 | this->other_host->get_string(this->other_host) : "(unknown)"; | |
1741 | my_id = this->my_id ? | |
1742 | this->my_id->get_string(this->my_id) : "(unknown)"; | |
1743 | other_id = this->other_id ? | |
1744 | this->other_id->get_string(this->other_id) : "(unknown)"; | |
1745 | ||
1746 | this->logger->log(this->logger, AUDIT, | |
1747 | "IKE_SA deleted between %s[%s]...%s[%s]", | |
1748 | my_host, my_id, other_host, other_id); | |
1749 | ||
1750 | DESTROY_IF(this->my_host); | |
1751 | DESTROY_IF(this->other_host); | |
1752 | DESTROY_IF(this->my_id); | |
1753 | DESTROY_IF(this->other_id); | |
1754 | ||
1755 | free(this->name); | |
87a217f9 | 1756 | this->ike_sa_id->destroy(this->ike_sa_id); |
5113680f | 1757 | free(this); |
7ba38761 JH |
1758 | } |
1759 | ||
1760 | /* | |
39b2903f | 1761 | * Described in header. |
7ba38761 JH |
1762 | */ |
1763 | ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) | |
1764 | { | |
5113680f | 1765 | private_ike_sa_t *this = malloc_thing(private_ike_sa_t); |
8d77edde | 1766 | |
7ba38761 | 1767 | /* Public functions */ |
3dd3c5f3 MW |
1768 | this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state; |
1769 | this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state; | |
8dfbe71b MW |
1770 | this->public.get_name = (char*(*)(ike_sa_t*))get_name; |
1771 | this->public.set_name = (void(*)(ike_sa_t*,char*))set_name; | |
3dd3c5f3 | 1772 | this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; |
8dfbe71b | 1773 | this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate; |
45f76a7d MW |
1774 | this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route; |
1775 | this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute; | |
8dfbe71b | 1776 | this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire; |
3dd3c5f3 | 1777 | this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; |
8dfbe71b MW |
1778 | this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host; |
1779 | this->public.get_other_host = (host_t*(*)(ike_sa_t*)) get_other_host; | |
1780 | this->public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id; | |
1781 | this->public.set_my_id = (void(*)(ike_sa_t*,identification_t*)) set_my_id; | |
1782 | this->public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id; | |
1783 | this->public.set_other_id = (void(*)(ike_sa_t*,identification_t*)) set_other_id; | |
bcb95ced | 1784 | this->public.get_next_message_id = (u_int32_t(*)(ike_sa_t*)) get_next_message_id; |
3dd3c5f3 MW |
1785 | this->public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request; |
1786 | this->public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status; | |
1787 | this->public.delete = (status_t(*)(ike_sa_t*))delete_; | |
1788 | this->public.destroy = (void(*)(ike_sa_t*))destroy; | |
1789 | this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd; | |
1790 | this->public.send_keepalive = (void (*)(ike_sa_t*)) send_keepalive; | |
1791 | this->public.get_prf = (prf_t *(*) (ike_sa_t *)) get_prf; | |
1792 | this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf; | |
1793 | this->public.get_prf_auth_i = (prf_t *(*) (ike_sa_t *)) get_prf_auth_i; | |
1794 | this->public.get_prf_auth_r = (prf_t *(*) (ike_sa_t *)) get_prf_auth_r; | |
3dd3c5f3 | 1795 | this->public.build_transforms = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool)) build_transforms; |
698d7749 | 1796 | this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa; |
45f76a7d | 1797 | this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa; |
698d7749 MW |
1798 | this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa; |
1799 | this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa; | |
1800 | this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa; | |
1801 | this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa; | |
3dd3c5f3 MW |
1802 | this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt; |
1803 | this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled; | |
1804 | ||
7ba38761 | 1805 | /* initialize private fields */ |
5113680f | 1806 | this->logger = logger_manager->get_logger(logger_manager, IKE_SA); |
d048df5c | 1807 | this->ike_sa_id = ike_sa_id->clone(ike_sa_id); |
8dfbe71b | 1808 | this->name = strdup("(uninitialized)"); |
7ba38761 | 1809 | this->child_sas = linked_list_create(); |
45f76a7d MW |
1810 | this->my_host = host_create(AF_INET, "0.0.0.0", 0); |
1811 | this->other_host = host_create(AF_INET, "0.0.0.0", 0); | |
1812 | this->my_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER); | |
1813 | this->other_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER); | |
3dd3c5f3 MW |
1814 | this->crypter_in = NULL; |
1815 | this->crypter_out = NULL; | |
1816 | this->signer_in = NULL; | |
1817 | this->signer_out = NULL; | |
b4e3d1dd | 1818 | this->prf = NULL; |
ce461bbd MW |
1819 | this->prf_auth_i = NULL; |
1820 | this->prf_auth_r = NULL; | |
971218c3 | 1821 | this->child_prf = NULL; |
1396815a MW |
1822 | this->nat_here = FALSE; |
1823 | this->nat_there = FALSE; | |
3dd3c5f3 MW |
1824 | this->transaction_queue = linked_list_create(); |
1825 | this->transaction_in = NULL; | |
1826 | this->transaction_in_next = NULL; | |
1827 | this->transaction_out = NULL; | |
bcb95ced MW |
1828 | this->state = IKE_CREATED; |
1829 | /* we start with message ID out, as ike_sa_init does not use this counter */ | |
3dd3c5f3 MW |
1830 | this->message_id_out = 0; |
1831 | this->time_inbound = 0; | |
1832 | this->time_outbound = 0; | |
1833 | ||
1834 | return &this->public; | |
7ba38761 | 1835 | } |