fixed SPI when rekeying and deleting CHILD_SAs
[strongswan.git] / src / charon / sa / child_sa.c
1 /**
2 * @file child_sa.c
3 *
4 * @brief Implementation of child_sa_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <netdb.h>
24
25 #include "child_sa.h"
26
27 #include <daemon.h>
28
29
30 typedef struct sa_policy_t sa_policy_t;
31
32 /**
33 * Struct used to store information for a policy. This
34 * is needed since we must provide all this information
35 * for deleting a policy...
36 */
37 struct sa_policy_t {
38
39 struct {
40 /** subnet address behind peer peer */
41 host_t *net;
42 /** netmask used for net */
43 u_int8_t net_mask;
44 } me, other;
45
46 /**
47 * Protocol for this policy, such as TCP/UDP/ICMP...
48 */
49 int upper_proto;
50 };
51
52 typedef struct private_child_sa_t private_child_sa_t;
53
54 /**
55 * Private data of a child_sa_t object.
56 */
57 struct private_child_sa_t {
58 /**
59 * Public interface of child_sa_t.
60 */
61 child_sa_t public;
62
63 struct {
64 /** address of peer */
65 host_t *addr;
66 /** actual used SPI, 0 if unused */
67 u_int32_t spi;
68 } me, other;
69
70 /**
71 * Allocated SPI for a ESP proposal candidates
72 */
73 u_int32_t alloc_esp_spi;
74
75 /**
76 * Allocated SPI for a AH proposal candidates
77 */
78 u_int32_t alloc_ah_spi;
79
80 /**
81 * Protocol used to protect this SA, ESP|AH
82 */
83 protocol_id_t protocol;
84
85 /**
86 * List containing sa_policy_t objects
87 */
88 linked_list_t *policies;
89
90 /**
91 * reqid used for this child_sa
92 */
93 u_int32_t reqid;
94
95 /**
96 * time, on which SA was installed
97 */
98 time_t install_time;
99
100 /**
101 * Lifetime before rekeying
102 */
103 u_int32_t soft_lifetime;
104
105 /**
106 * Lifetime before delete
107 */
108 u_int32_t hard_lifetime;
109
110 /**
111 * has this CHILD_SA been rekeyed?
112 */
113 bool rekeyed;
114
115 /**
116 * CHILD_SAs own logger
117 */
118 logger_t *logger;
119 };
120
121 /**
122 * Implements child_sa_t.get_reqid
123 */
124 static u_int32_t get_reqid(private_child_sa_t *this)
125 {
126 return this->reqid;
127 }
128
129 /**
130 * Implements child_sa_t.get_spi
131 */
132 u_int32_t get_spi(private_child_sa_t *this, bool inbound)
133 {
134 if (inbound)
135 {
136 return this->me.spi;
137 }
138 return this->other.spi;
139 }
140
141 /**
142 * Implements child_sa_t.get_protocol
143 */
144 protocol_id_t get_protocol(private_child_sa_t *this)
145 {
146 return this->protocol;
147 }
148
149 /**
150 * Implements child_sa_t.alloc
151 */
152 static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
153 {
154 protocol_id_t protocol;
155 iterator_t *iterator;
156 proposal_t *proposal;
157
158 /* iterator through proposals to update spis */
159 iterator = proposals->create_iterator(proposals, TRUE);
160 while(iterator->has_next(iterator))
161 {
162 iterator->current(iterator, (void**)&proposal);
163 protocol = proposal->get_protocol(proposal);
164
165 if (protocol == PROTO_AH)
166 {
167 /* get a new spi for AH, if not already done */
168 if (this->alloc_ah_spi == 0)
169 {
170 if (charon->kernel_interface->get_spi(
171 charon->kernel_interface,
172 this->other.addr, this->me.addr,
173 PROTO_AH, this->reqid,
174 &this->alloc_ah_spi) != SUCCESS)
175 {
176 return FAILED;
177 }
178 }
179 proposal->set_spi(proposal, this->alloc_ah_spi);
180 }
181 if (protocol == PROTO_ESP)
182 {
183 /* get a new spi for ESP, if not already done */
184 if (this->alloc_esp_spi == 0)
185 {
186 if (charon->kernel_interface->get_spi(
187 charon->kernel_interface,
188 this->other.addr, this->me.addr,
189 PROTO_ESP, this->reqid,
190 &this->alloc_esp_spi) != SUCCESS)
191 {
192 return FAILED;
193 }
194 }
195 proposal->set_spi(proposal, this->alloc_esp_spi);
196 }
197
198 }
199 iterator->destroy(iterator);
200 return SUCCESS;
201 }
202
203 static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus, bool mine)
204 {
205 u_int32_t spi;
206 algorithm_t *enc_algo, *int_algo;
207 algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0};
208 algorithm_t int_algo_none = {AUTH_UNDEFINED, 0};
209 host_t *src;
210 host_t *dst;
211 status_t status;
212
213 this->protocol = proposal->get_protocol(proposal);
214
215 /* now we have to decide which spi to use. Use self allocated, if "mine",
216 * or the one in the proposal, if not "mine" (others). Additionally,
217 * source and dest host switch depending on the role */
218 if (mine)
219 {
220 /* if we have allocated SPIs for AH and ESP, we must delete the unused
221 * one. */
222 if (this->protocol == PROTO_ESP)
223 {
224 this->me.spi = this->alloc_esp_spi;
225 if (this->alloc_ah_spi)
226 {
227 charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr,
228 this->alloc_ah_spi, PROTO_AH);
229 }
230 }
231 else
232 {
233 this->me.spi = this->alloc_ah_spi;
234 if (this->alloc_esp_spi)
235 {
236 charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr,
237 this->alloc_esp_spi, PROTO_ESP);
238 }
239 }
240 spi = this->me.spi;
241 dst = this->me.addr;
242 src = this->other.addr;
243 }
244 else
245 {
246 this->other.spi = proposal->get_spi(proposal);
247 spi = this->other.spi;
248 src = this->me.addr;
249 dst = this->other.addr;
250 }
251
252 this->logger->log(this->logger, CONTROL|LEVEL1, "Adding %s %s SA",
253 mine ? "inbound" : "outbound",
254 mapping_find(protocol_id_m, this->protocol));
255
256 /* select encryption algo */
257 if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_algo))
258 {
259 this->logger->log(this->logger, CONTROL|LEVEL2, " using %s for encryption",
260 mapping_find(encryption_algorithm_m, enc_algo->algorithm));
261 }
262 else
263 {
264 enc_algo = &enc_algo_none;
265 }
266
267 /* select integrity algo */
268 if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_algo))
269 {
270 this->logger->log(this->logger, CONTROL|LEVEL2, " using %s for integrity",
271 mapping_find(integrity_algorithm_m, int_algo->algorithm));
272 }
273 else
274 {
275 int_algo = &int_algo_none;
276 }
277
278 /* send SA down to the kernel */
279 this->logger->log(this->logger, CONTROL|LEVEL2,
280 " SPI 0x%.8x, src %s dst %s",
281 ntohl(spi), src->get_address(src), dst->get_address(dst));
282 status = charon->kernel_interface->add_sa(charon->kernel_interface,
283 src, dst,
284 spi, this->protocol,
285 this->reqid,
286 mine ? 0 : this->soft_lifetime,
287 this->hard_lifetime,
288 enc_algo, int_algo, prf_plus, mine);
289
290 this->install_time = time(NULL);
291
292 return status;
293 }
294
295 static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
296 {
297 linked_list_t *list;
298 u_int32_t outbound_spi, inbound_spi;
299
300 /* backup outbound spi, as alloc overwrites it */
301 outbound_spi = proposal->get_spi(proposal);
302
303 /* get SPIs inbound SAs */
304 list = linked_list_create();
305 list->insert_last(list, proposal);
306 if (alloc(this, list) != SUCCESS)
307 {
308 list->destroy(list);
309 return FAILED;
310 }
311 list->destroy(list);
312 inbound_spi = proposal->get_spi(proposal);
313
314 /* install inbound SAs */
315 if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
316 {
317 return FAILED;
318 }
319
320 /* install outbound SAs, restore spi*/
321 proposal->set_spi(proposal, outbound_spi);
322 if (install(this, proposal, prf_plus, FALSE) != SUCCESS)
323 {
324 return FAILED;
325 }
326 proposal->set_spi(proposal, inbound_spi);
327
328 return SUCCESS;
329 }
330
331 static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
332 {
333 u_int32_t inbound_spi;
334
335 /* backup received spi, as install() overwrites it */
336 inbound_spi = proposal->get_spi(proposal);
337
338 /* install outbound SAs */
339 if (install(this, proposal, prf_plus, FALSE) != SUCCESS)
340 {
341 return FAILED;
342 }
343
344 /* restore spi */
345 proposal->set_spi(proposal, inbound_spi);
346 /* install inbound SAs */
347 if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
348 {
349 return FAILED;
350 }
351
352 return SUCCESS;
353 }
354
355 static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list)
356 {
357 iterator_t *my_iter, *other_iter;
358 traffic_selector_t *my_ts, *other_ts;
359
360 /* iterate over both lists */
361 my_iter = my_ts_list->create_iterator(my_ts_list, TRUE);
362 other_iter = other_ts_list->create_iterator(other_ts_list, TRUE);
363 while (my_iter->has_next(my_iter))
364 {
365 my_iter->current(my_iter, (void**)&my_ts);
366 other_iter->reset(other_iter);
367 while (other_iter->has_next(other_iter))
368 {
369 /* set up policies for every entry in my_ts_list to every entry in other_ts_list */
370 int family;
371 chunk_t from_addr;
372 u_int16_t from_port, to_port;
373 sa_policy_t *policy;
374 status_t status;
375
376 other_iter->current(other_iter, (void**)&other_ts);
377
378 /* only set up policies if protocol matches */
379 if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
380 {
381 continue;
382 }
383 policy = malloc_thing(sa_policy_t);
384 policy->upper_proto = my_ts->get_protocol(my_ts);
385
386 /* calculate net and ports for local side */
387 family = my_ts->get_type(my_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
388 from_addr = my_ts->get_from_address(my_ts);
389 from_port = my_ts->get_from_port(my_ts);
390 to_port = my_ts->get_to_port(my_ts);
391 from_port = (from_port != to_port) ? 0 : from_port;
392 policy->me.net = host_create_from_chunk(family, from_addr, from_port);
393 policy->me.net_mask = my_ts->get_netmask(my_ts);
394 chunk_free(&from_addr);
395
396 /* calculate net and ports for remote side */
397 family = other_ts->get_type(other_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
398 from_addr = other_ts->get_from_address(other_ts);
399 from_port = other_ts->get_from_port(other_ts);
400 to_port = other_ts->get_to_port(other_ts);
401 from_port = (from_port != to_port) ? 0 : from_port;
402 policy->other.net = host_create_from_chunk(family, from_addr, from_port);
403 policy->other.net_mask = other_ts->get_netmask(other_ts);
404 chunk_free(&from_addr);
405
406 /* install 3 policies: out, in and forward */
407 status = charon->kernel_interface->add_policy(charon->kernel_interface,
408 this->me.addr, this->other.addr,
409 policy->me.net, policy->other.net,
410 policy->me.net_mask, policy->other.net_mask,
411 XFRM_POLICY_OUT, policy->upper_proto,
412 this->protocol,
413 this->reqid);
414
415 status |= charon->kernel_interface->add_policy(charon->kernel_interface,
416 this->other.addr, this->me.addr,
417 policy->other.net, policy->me.net,
418 policy->other.net_mask, policy->me.net_mask,
419 XFRM_POLICY_IN, policy->upper_proto,
420 this->protocol,
421 this->reqid);
422
423 status |= charon->kernel_interface->add_policy(charon->kernel_interface,
424 this->other.addr, this->me.addr,
425 policy->other.net, policy->me.net,
426 policy->other.net_mask, policy->me.net_mask,
427 XFRM_POLICY_FWD, policy->upper_proto,
428 this->protocol,
429 this->reqid);
430
431 if (status != SUCCESS)
432 {
433 my_iter->destroy(my_iter);
434 other_iter->destroy(other_iter);
435 policy->me.net->destroy(policy->me.net);
436 policy->other.net->destroy(policy->other.net);
437 free(policy);
438 return status;
439 }
440
441 /* add it to the policy list, since we want to know which policies we own */
442 this->policies->insert_last(this->policies, policy);
443 }
444 }
445 my_iter->destroy(my_iter);
446 other_iter->destroy(other_iter);
447 return SUCCESS;
448 }
449
450 /**
451 * Implementation of child_sa_t.set_rekeyed.
452 */
453 static void set_rekeyed(private_child_sa_t *this)
454 {
455 this->rekeyed = TRUE;
456 }
457
458 /**
459 * Implementation of child_sa_t.log_status.
460 */
461 static void log_status(private_child_sa_t *this, logger_t *logger, char* name)
462 {
463 iterator_t *iterator;
464 sa_policy_t *policy;
465 struct protoent *proto;
466 char proto_buf[8] = "";
467 char *proto_name = proto_buf;
468
469 if (logger == NULL)
470 {
471 logger = this->logger;
472 }
473 if (this->soft_lifetime)
474 {
475 logger->log(logger, CONTROL|LEVEL1, " \"%s\": protected with %s (0x%x/0x%x), reqid %d, rekeying in %ds:",
476 name,
477 this->protocol == PROTO_ESP ? "ESP" : "AH",
478 htonl(this->me.spi), htonl(this->other.spi),
479 this->reqid,
480 this->soft_lifetime - (time(NULL) - this->install_time));
481 }
482 else
483 {
484
485 logger->log(logger, CONTROL|LEVEL1, " \"%s\": protected with %s (0x%x/0x%x), reqid %d, no rekeying:",
486 name,
487 this->protocol == PROTO_ESP ? "ESP" : "AH",
488 htonl(this->me.spi), htonl(this->other.spi),
489 this->reqid);
490 }
491 iterator = this->policies->create_iterator(this->policies, TRUE);
492 while (iterator->has_next(iterator))
493 {
494 iterator->current(iterator, (void**)&policy);
495 if (policy->upper_proto)
496 {
497 proto = getprotobynumber(policy->upper_proto);
498 if (proto)
499 {
500 proto_name = proto->p_name;
501 }
502 else
503 {
504 snprintf(proto_buf, sizeof(proto_buf), "<%d>", policy->upper_proto);
505 }
506 }
507 logger->log(logger, CONTROL, " \"%s\": %s/%d==%s==%s/%d",
508 name,
509 policy->me.net->get_address(policy->me.net), policy->me.net_mask,
510 proto_name,
511 policy->other.net->get_address(policy->other.net), policy->other.net_mask);
512 }
513 iterator->destroy(iterator);
514 }
515
516 /**
517 * Implementation of child_sa_t.destroy.
518 */
519 static void destroy(private_child_sa_t *this)
520 {
521 sa_policy_t *policy;
522
523 /* delete SAs in the kernel, if they are set up */
524 if (this->me.spi)
525 {
526 charon->kernel_interface->del_sa(charon->kernel_interface,
527 this->me.addr, this->me.spi, this->protocol);
528 }
529 if (this->alloc_esp_spi && this->alloc_esp_spi != this->me.spi)
530 {
531 charon->kernel_interface->del_sa(charon->kernel_interface,
532 this->me.addr, this->alloc_esp_spi, PROTO_ESP);
533 }
534 if (this->alloc_ah_spi && this->alloc_ah_spi != this->me.spi)
535 {
536 charon->kernel_interface->del_sa(charon->kernel_interface,
537 this->me.addr, this->alloc_ah_spi, PROTO_AH);
538 }
539 if (this->other.spi)
540 {
541 charon->kernel_interface->del_sa(charon->kernel_interface,
542 this->other.addr, this->other.spi, this->protocol);
543 }
544
545 /* delete all policies in the kernel */
546 while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS)
547 {
548 if (!this->rekeyed)
549 {
550 /* let rekeyed policies, as they are used by another child_sa */
551 charon->kernel_interface->del_policy(charon->kernel_interface,
552 this->me.addr, this->other.addr,
553 policy->me.net, policy->other.net,
554 policy->me.net_mask, policy->other.net_mask,
555 XFRM_POLICY_OUT, policy->upper_proto);
556
557 charon->kernel_interface->del_policy(charon->kernel_interface,
558 this->other.addr, this->me.addr,
559 policy->other.net, policy->me.net,
560 policy->other.net_mask, policy->me.net_mask,
561 XFRM_POLICY_IN, policy->upper_proto);
562
563 charon->kernel_interface->del_policy(charon->kernel_interface,
564 this->other.addr, this->me.addr,
565 policy->other.net, policy->me.net,
566 policy->other.net_mask, policy->me.net_mask,
567 XFRM_POLICY_FWD, policy->upper_proto);
568 }
569 policy->me.net->destroy(policy->me.net);
570 policy->other.net->destroy(policy->other.net);
571 free(policy);
572 }
573 this->policies->destroy(this->policies);
574
575 free(this);
576 }
577
578 /*
579 * Described in header.
580 */
581 child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
582 u_int32_t soft_lifetime, u_int32_t hard_lifetime)
583 {
584 static u_int32_t reqid = 2000000000;
585 private_child_sa_t *this = malloc_thing(private_child_sa_t);
586
587 /* public functions */
588 this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid;
589 this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi;
590 this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol;
591 this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc;
592 this->public.add = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))add;
593 this->public.update = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))update;
594 this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies;
595 this->public.set_rekeyed = (void (*)(child_sa_t*))set_rekeyed;
596 this->public.log_status = (void (*)(child_sa_t*, logger_t*, char*))log_status;
597 this->public.destroy = (void(*)(child_sa_t*))destroy;
598
599 /* private data */
600 this->logger = logger_manager->get_logger(logger_manager, CHILD_SA);
601 this->me.addr = me;
602 this->other.addr = other;
603 this->me.spi = 0;
604 this->other.spi = 0;
605 this->alloc_ah_spi = 0;
606 this->alloc_esp_spi = 0;
607 this->soft_lifetime = soft_lifetime;
608 this->hard_lifetime = hard_lifetime;
609 /* reuse old reqid if we are rekeying an existing CHILD_SA */
610 this->reqid = rekey ? rekey : ++reqid;
611 this->policies = linked_list_create();
612 this->protocol = PROTO_NONE;
613 this->rekeyed = FALSE;
614
615 return (&this->public);
616 }