fd82f123b93afc807ee4fd862c3231f8fb2bcce7
[strongswan.git] / Source / 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 "child_sa.h"
24
25
26 #include <daemon.h>
27
28
29 typedef struct sa_policy_t sa_policy_t;
30
31 /**
32 * Struct used to store information for a policy. This
33 * is needed since we must provide all this information
34 * for deleting a policy...
35 */
36 struct sa_policy_t {
37
38 /**
39 * Network on local side
40 */
41 host_t *my_net;
42
43 /**
44 * Network on remote side
45 */
46 host_t *other_net;
47
48 /**
49 * Number of bits for local network (subnet size)
50 */
51 u_int8_t my_net_mask;
52
53 /**
54 * Number of bits for remote network (subnet size)
55 */
56 u_int8_t other_net_mask;
57
58 /**
59 * Protocol for this policy, such as TCP/UDP/ICMP...
60 */
61 int upper_proto;
62 };
63
64 typedef struct private_child_sa_t private_child_sa_t;
65
66 /**
67 * Private data of a child_sa_t object.
68 */
69 struct private_child_sa_t {
70 /**
71 * Public interface of child_sa_t.
72 */
73 child_sa_t public;
74
75 /**
76 * IP of this peer
77 */
78 host_t *me;
79
80 /**
81 * IP of other peer
82 */
83 host_t *other;
84
85 /**
86 * Local security parameter index for AH protocol, 0 if not used
87 */
88 u_int32_t my_ah_spi;
89
90 /**
91 * Local security parameter index for ESP protocol, 0 if not used
92 */
93 u_int32_t my_esp_spi;
94
95 /**
96 * Remote security parameter index for AH protocol, 0 if not used
97 */
98 u_int32_t other_ah_spi;
99
100 /**
101 * Remote security parameter index for ESP protocol, 0 if not used
102 */
103 u_int32_t other_esp_spi;
104
105 /**
106 * List containing policy_id_t objects
107 */
108 linked_list_t *policies;
109
110 /**
111 * reqid used for this child_sa
112 */
113 u_int32_t reqid;
114
115 /**
116 * CHILD_SAs own logger
117 */
118 logger_t *logger;
119 };
120
121 /**
122 * Implements child_sa_t.alloc
123 */
124 static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
125 {
126 protocol_id_t protocols[2];
127 iterator_t *iterator;
128 proposal_t *proposal;
129 status_t status;
130 u_int i;
131
132 /* iterator through proposals */
133 iterator = proposals->create_iterator(proposals, TRUE);
134 while(iterator->has_next(iterator))
135 {
136 iterator->current(iterator, (void**)&proposal);
137 proposal->get_protocols(proposal, protocols);
138
139 /* check all protocols */
140 for (i = 0; i<2; i++)
141 {
142 switch (protocols[i])
143 {
144 case PROTO_AH:
145 /* do we already have an spi for AH?*/
146 if (this->my_ah_spi == 0)
147 {
148 /* nope, get one */
149 status = charon->kernel_interface->get_spi(
150 charon->kernel_interface,
151 this->me, this->other,
152 PROTO_AH, FALSE,
153 &(this->my_ah_spi));
154 }
155 /* update proposal */
156 proposal->set_spi(proposal, PROTO_AH, (u_int64_t)this->my_ah_spi);
157 break;
158 case PROTO_ESP:
159 /* do we already have an spi for ESP?*/
160 if (this->my_esp_spi == 0)
161 {
162 /* nope, get one */
163 status = charon->kernel_interface->get_spi(
164 charon->kernel_interface,
165 this->me, this->other,
166 PROTO_ESP, FALSE,
167 &(this->my_esp_spi));
168 }
169 /* update proposal */
170 proposal->set_spi(proposal, PROTO_ESP, (u_int64_t)this->my_esp_spi);
171 break;
172 default:
173 break;
174 }
175 if (status != SUCCESS)
176 {
177 iterator->destroy(iterator);
178 return FAILED;
179 }
180 }
181 }
182 iterator->destroy(iterator);
183 return SUCCESS;
184 }
185
186 static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus, bool mine)
187 {
188 protocol_id_t protocols[2];
189 u_int32_t spi;
190 encryption_algorithm_t enc_algo;
191 integrity_algorithm_t int_algo;
192 chunk_t enc_key, int_key;
193 algorithm_t *algo;
194 crypter_t *crypter;
195 signer_t *signer;
196 size_t key_size;
197 host_t *src;
198 host_t *dst;
199 status_t status;
200 u_int i;
201
202 /* we must assign the roles to correctly set up the SAs */
203 if (mine)
204 {
205 src = this->me;
206 dst = this->other;
207 }
208 else
209 {
210 dst = this->me;
211 src = this->other;
212 }
213
214 proposal->get_protocols(proposal, protocols);
215 /* derive keys in order as protocols appear */
216 for (i = 0; i<2; i++)
217 {
218 if (protocols[i] != PROTO_NONE)
219 {
220
221 /* now we have to decide which spi to use. Use self allocated, if "mine",
222 * or the one in the proposal, if not "mine" (others). */
223 if (mine)
224 {
225 if (protocols[i] == PROTO_AH)
226 {
227 spi = this->my_ah_spi;
228 }
229 else
230 {
231 spi = this->my_esp_spi;
232 }
233 }
234 else /* use proposals spi */
235 {
236 spi = proposal->get_spi(proposal, protocols[i]);
237 if (protocols[i] == PROTO_AH)
238 {
239 this->other_ah_spi = spi;
240 }
241 else
242 {
243 this->other_esp_spi = spi;
244 }
245 }
246
247 /* derive encryption key first */
248 if (proposal->get_algorithm(proposal, protocols[i], ENCRYPTION_ALGORITHM, &algo))
249 {
250 enc_algo = algo->algorithm;
251 this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s, ",
252 mapping_find(protocol_id_m, protocols[i]),
253 mine ? "me" : "other",
254 mapping_find(transform_type_m, ENCRYPTION_ALGORITHM),
255 mapping_find(encryption_algorithm_m, enc_algo));
256
257 /* we must create a (unused) crypter, since its the only way to get the size
258 * of the key. This is not so nice, since charon must support all algorithms
259 * the kernel supports...
260 * TODO: build something of a encryption algorithm lookup function
261 */
262 crypter = crypter_create(enc_algo, algo->key_size);
263 key_size = crypter->get_key_size(crypter);
264 crypter->destroy(crypter);
265 prf_plus->allocate_bytes(prf_plus, key_size, &enc_key);
266 this->logger->log_chunk(this->logger, PRIVATE, "key:", enc_key);
267 }
268 else
269 {
270 enc_algo = ENCR_UNDEFINED;
271 }
272
273 /* derive integrity key */
274 if (proposal->get_algorithm(proposal, protocols[i], INTEGRITY_ALGORITHM, &algo))
275 {
276 int_algo = algo->algorithm;
277 this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s,",
278 mapping_find(protocol_id_m, protocols[i]),
279 mine ? "me" : "other",
280 mapping_find(transform_type_m, INTEGRITY_ALGORITHM),
281 mapping_find(integrity_algorithm_m, algo->algorithm));
282
283 signer = signer_create(int_algo);
284 key_size = signer->get_key_size(signer);
285 signer->destroy(signer);
286 prf_plus->allocate_bytes(prf_plus, key_size, &int_key);
287 this->logger->log_chunk(this->logger, PRIVATE, "key:", int_key);
288 }
289 else
290 {
291 int_algo = AUTH_UNDEFINED;
292 }
293 /* send keys down to kernel */
294 this->logger->log(this->logger, CONTROL|LEVEL1,
295 "installing 0x%.8x for %s, src %s dst %s",
296 ntohl(spi), mapping_find(protocol_id_m, protocols[i]),
297 src->get_address(src), dst->get_address(dst));
298 status = charon->kernel_interface->add_sa(charon->kernel_interface,
299 src, dst,
300 spi, protocols[i],
301 this->reqid,
302 enc_algo, enc_key,
303 int_algo, int_key, mine);
304 /* clean up for next round */
305 if (enc_algo != ENCR_UNDEFINED)
306 {
307 chunk_free(&enc_key);
308 }
309 if (int_algo != AUTH_UNDEFINED)
310 {
311 chunk_free(&int_key);
312 }
313
314 if (status != SUCCESS)
315 {
316 return FAILED;
317 }
318
319
320 }
321 }
322 return SUCCESS;
323 }
324
325 static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
326 {
327 linked_list_t *list;
328
329 /* install others (initiators) SAs*/
330 if (install(this, proposal, prf_plus, FALSE) != SUCCESS)
331 {
332 return FAILED;
333 }
334
335 /* get SPIs for our SAs */
336 list = linked_list_create();
337 list->insert_last(list, proposal);
338 if (alloc(this, list) != SUCCESS)
339 {
340 list->destroy(list);
341 return FAILED;
342 }
343 list->destroy(list);
344
345 /* install our (responders) SAs */
346 if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
347 {
348 return FAILED;
349 }
350
351 return SUCCESS;
352 }
353
354 static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
355 {
356 /* install our (initator) SAs */
357 if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
358 {
359 return FAILED;
360 }
361 /* install his (responder) SAs */
362 if (install(this, proposal, prf_plus, FALSE) != SUCCESS)
363 {
364 return FAILED;
365 }
366
367 return SUCCESS;
368 }
369
370 static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list)
371 {
372 iterator_t *my_iter, *other_iter;
373 traffic_selector_t *my_ts, *other_ts;
374
375 /* iterate over both lists */
376 my_iter = my_ts_list->create_iterator(my_ts_list, TRUE);
377 other_iter = other_ts_list->create_iterator(other_ts_list, TRUE);
378 while (my_iter->has_next(my_iter))
379 {
380 my_iter->current(my_iter, (void**)&my_ts);
381 other_iter->reset(other_iter);
382 while (other_iter->has_next(other_iter))
383 {
384 /* set up policies for every entry in my_ts_list to every entry in other_ts_list */
385 int family;
386 chunk_t from_addr;
387 u_int16_t from_port, to_port;
388 sa_policy_t *policy;
389 status_t status;
390
391 other_iter->current(other_iter, (void**)&other_ts);
392
393 /* only set up policies if protocol matches */
394 if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
395 {
396 continue;
397 }
398 policy = malloc_thing(sa_policy_t);
399 policy->upper_proto = my_ts->get_protocol(my_ts);
400
401 /* calculate net and ports for local side */
402 family = my_ts->get_type(my_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
403 from_addr = my_ts->get_from_address(my_ts);
404 from_port = my_ts->get_from_port(my_ts);
405 to_port = my_ts->get_to_port(my_ts);
406 from_port = (from_port != to_port) ? 0 : from_port;
407 policy->my_net = host_create_from_chunk(family, from_addr, from_port);
408 policy->my_net_mask = my_ts->get_netmask(my_ts);
409 chunk_free(&from_addr);
410
411 /* calculate net and ports for remote side */
412 family = other_ts->get_type(other_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
413 from_addr = other_ts->get_from_address(other_ts);
414 from_port = other_ts->get_from_port(other_ts);
415 to_port = other_ts->get_to_port(other_ts);
416 from_port = (from_port != to_port) ? 0 : from_port;
417 policy->other_net = host_create_from_chunk(family, from_addr, from_port);
418 policy->other_net_mask = other_ts->get_netmask(other_ts);
419 chunk_free(&from_addr);
420
421 /* install 3 policies: out, in and forward */
422 status = charon->kernel_interface->add_policy(charon->kernel_interface,
423 this->me, this->other,
424 policy->my_net, policy->other_net,
425 policy->my_net_mask, policy->other_net_mask,
426 XFRM_POLICY_OUT, policy->upper_proto,
427 this->my_ah_spi, this->my_esp_spi,
428 this->reqid);
429
430 status |= charon->kernel_interface->add_policy(charon->kernel_interface,
431 this->other, this->me,
432 policy->other_net, policy->my_net,
433 policy->other_net_mask, policy->my_net_mask,
434 XFRM_POLICY_IN, policy->upper_proto,
435 this->my_ah_spi, this->my_esp_spi,
436 this->reqid);
437
438 status |= charon->kernel_interface->add_policy(charon->kernel_interface,
439 this->other, this->me,
440 policy->other_net, policy->my_net,
441 policy->other_net_mask, policy->my_net_mask,
442 XFRM_POLICY_FWD, policy->upper_proto,
443 this->my_ah_spi, this->my_esp_spi,
444 this->reqid);
445
446 if (status != SUCCESS)
447 {
448 my_iter->destroy(my_iter);
449 other_iter->destroy(other_iter);
450 free(policy);
451 return status;
452 }
453
454 /* add it to the policy list, since we want to know which policies we own */
455 this->policies->insert_last(this->policies, policy);
456 }
457 }
458
459 my_iter->destroy(my_iter);
460 other_iter->destroy(other_iter);
461 return SUCCESS;
462 }
463
464 /**
465 * Implementation of child_sa_t.destroy.
466 */
467 static void destroy(private_child_sa_t *this)
468 {
469 /* delete all policys in the kernel */
470 sa_policy_t *policy;
471 while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS)
472 {
473 charon->kernel_interface->del_policy(charon->kernel_interface,
474 this->me, this->other,
475 policy->my_net, policy->other_net,
476 policy->my_net_mask, policy->other_net_mask,
477 XFRM_POLICY_OUT, policy->upper_proto);
478
479 charon->kernel_interface->del_policy(charon->kernel_interface,
480 this->other, this->me,
481 policy->other_net, policy->my_net,
482 policy->other_net_mask, policy->my_net_mask,
483 XFRM_POLICY_IN, policy->upper_proto);
484
485 charon->kernel_interface->del_policy(charon->kernel_interface,
486 this->other, this->me,
487 policy->other_net, policy->my_net,
488 policy->other_net_mask, policy->my_net_mask,
489 XFRM_POLICY_FWD, policy->upper_proto);
490
491 policy->my_net->destroy(policy->my_net);
492 policy->other_net->destroy(policy->other_net);
493 free(policy);
494 }
495 this->policies->destroy(this->policies);
496
497 /* delete SAs in the kernel, if they are set up */
498 if (this->my_ah_spi)
499 {
500 charon->kernel_interface->del_sa(charon->kernel_interface,
501 this->other, this->my_ah_spi, PROTO_AH);
502 charon->kernel_interface->del_sa(charon->kernel_interface,
503 this->me, this->other_ah_spi, PROTO_AH);
504 }
505 if (this->my_esp_spi)
506 {
507 charon->kernel_interface->del_sa(charon->kernel_interface,
508 this->other, this->my_esp_spi, PROTO_ESP);
509 charon->kernel_interface->del_sa(charon->kernel_interface,
510 this->me, this->other_esp_spi, PROTO_ESP);
511 }
512 free(this);
513 }
514
515 /*
516 * Described in header.
517 */
518 child_sa_t * child_sa_create(host_t *me, host_t* other)
519 {
520 static u_int32_t reqid = 0xc0000000;
521 private_child_sa_t *this = malloc_thing(private_child_sa_t);
522
523 /* public functions */
524 this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc;
525 this->public.add = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))add;
526 this->public.update = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))update;
527 this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies;
528 this->public.destroy = (void(*)(child_sa_t*))destroy;
529
530 /* private data */
531 this->logger = logger_manager->get_logger(logger_manager, CHILD_SA);
532 this->me = me;
533 this->other = other;
534 this->my_ah_spi = 0;
535 this->my_esp_spi = 0;
536 this->other_ah_spi = 0;
537 this->other_esp_spi = 0;
538 this->reqid = reqid++;
539 this->policies = linked_list_create();
540
541 return (&this->public);
542 }