- fixed policy setup bug
[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 <utils/allocator.h>
27 #include <daemon.h>
28
29
30 typedef struct private_child_sa_t private_child_sa_t;
31
32 /**
33 * Private data of a child_sa_t object.
34 */
35 struct private_child_sa_t {
36 /**
37 * Public interface of child_sa_t.
38 */
39 child_sa_t public;
40
41 /**
42 * IP of this peer
43 */
44 host_t *me;
45
46 /**
47 * IP of other peer
48 */
49 host_t *other;
50
51 /**
52 * Security parameter index for AH protocol, 0 if not used
53 */
54 u_int32_t ah_spi;
55
56 /**
57 * Security parameter index for ESP protocol, 0 if not used
58 */
59 u_int32_t esp_spi;
60
61 /**
62 * reqid used for this child_sa
63 */
64 u_int32_t reqid;
65
66 /**
67 * CHILD_SAs own logger
68 */
69 logger_t *logger;
70 };
71
72 /**
73 * Implements child_sa_t.alloc
74 */
75 static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
76 {
77 protocol_id_t protocols[2];
78 iterator_t *iterator;
79 proposal_t *proposal;
80 status_t status;
81 u_int i;
82
83 /* iterator through proposals */
84 iterator = proposals->create_iterator(proposals, TRUE);
85 while(iterator->has_next(iterator))
86 {
87 iterator->current(iterator, (void**)&proposal);
88 proposal->get_protocols(proposal, protocols);
89
90 /* check all protocols */
91 for (i = 0; i<2; i++)
92 {
93 switch (protocols[i])
94 {
95 case AH:
96 /* do we already have an spi for AH?*/
97 if (this->ah_spi == 0)
98 {
99 /* nope, get one */
100 status = charon->kernel_interface->get_spi(
101 charon->kernel_interface,
102 this->me, this->other,
103 AH, FALSE,
104 &(this->ah_spi));
105 }
106 /* update proposal */
107 proposal->set_spi(proposal, AH, (u_int64_t)this->ah_spi);
108 break;
109 case ESP:
110 /* do we already have an spi for ESP?*/
111 if (this->esp_spi == 0)
112 {
113 /* nope, get one */
114 status = charon->kernel_interface->get_spi(
115 charon->kernel_interface,
116 this->me, this->other,
117 ESP, FALSE,
118 &(this->esp_spi));
119 }
120 /* update proposal */
121 proposal->set_spi(proposal, ESP, (u_int64_t)this->esp_spi);
122 break;
123 default:
124 break;
125 }
126 if (status != SUCCESS)
127 {
128 iterator->destroy(iterator);
129 return FAILED;
130 }
131 }
132 }
133 iterator->destroy(iterator);
134 return SUCCESS;
135 }
136
137 static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus, bool mine)
138 {
139 protocol_id_t protocols[2];
140 u_int32_t spi;
141 encryption_algorithm_t enc_algo;
142 integrity_algorithm_t int_algo;
143 chunk_t enc_key, int_key;
144 algorithm_t *algo;
145 crypter_t *crypter;
146 signer_t *signer;
147 size_t key_size;
148 host_t *src;
149 host_t *dst;
150 status_t status;
151 u_int i;
152
153 /* we must assign the roles to correctly set up the SAs */
154 if (mine)
155 {
156 src = this->me;
157 dst = this->other;
158 }
159 else
160 {
161 dst = this->me;
162 src = this->other;
163 }
164
165 proposal->get_protocols(proposal, protocols);
166 /* derive keys in order as protocols appear */
167 for (i = 0; i<2; i++)
168 {
169 if (protocols[i] != UNDEFINED_PROTOCOL_ID)
170 {
171
172 /* now we have to decide which spi to use. Use self allocated, if "mine",
173 * or the one in the proposal, if not "mine" (others). */
174 if (mine)
175 {
176 if (protocols[i] == AH)
177 {
178 spi = this->ah_spi;
179 }
180 else
181 {
182 spi = this->esp_spi;
183 }
184 }
185 else /* use proposals spi */
186 {
187 spi = proposal->get_spi(proposal, protocols[i]);
188 }
189
190 /* derive encryption key first */
191 if (proposal->get_algorithm(proposal, protocols[i], ENCRYPTION_ALGORITHM, &algo))
192 {
193 enc_algo = algo->algorithm;
194 this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s, ",
195 mapping_find(protocol_id_m, protocols[i]),
196 mine ? "me" : "other",
197 mapping_find(transform_type_m, ENCRYPTION_ALGORITHM),
198 mapping_find(encryption_algorithm_m, enc_algo));
199
200 /* we must create a (unused) crypter, since its the only way to get the size
201 * of the key. This is not so nice, since charon must support all algorithms
202 * the kernel supports...
203 * TODO: build something of a encryption algorithm lookup function
204 */
205 crypter = crypter_create(enc_algo, algo->key_size);
206 key_size = crypter->get_key_size(crypter);
207 crypter->destroy(crypter);
208 prf_plus->allocate_bytes(prf_plus, key_size, &enc_key);
209 this->logger->log_chunk(this->logger, PRIVATE, "key:", &enc_key);
210 }
211 else
212 {
213 enc_algo = ENCR_UNDEFINED;
214 }
215
216 /* derive integrity key */
217 if (proposal->get_algorithm(proposal, protocols[i], INTEGRITY_ALGORITHM, &algo))
218 {
219 int_algo = algo->algorithm;
220 this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s,",
221 mapping_find(protocol_id_m, protocols[i]),
222 mine ? "me" : "other",
223 mapping_find(transform_type_m, INTEGRITY_ALGORITHM),
224 mapping_find(integrity_algorithm_m, algo->algorithm));
225
226 signer = signer_create(int_algo);
227 key_size = signer->get_key_size(signer);
228 signer->destroy(signer);
229 prf_plus->allocate_bytes(prf_plus, key_size, &int_key);
230 this->logger->log_chunk(this->logger, PRIVATE, "key:", &int_key);
231 }
232 else
233 {
234 int_algo = AUTH_UNDEFINED;
235 }
236 /* send keys down to kernel */
237 this->logger->log(this->logger, CONTROL|LEVEL1,
238 "installing 0x%.8x for %s, src %s dst %s",
239 ntohl(spi), mapping_find(protocol_id_m, protocols[i]),
240 src->get_address(src), dst->get_address(dst));
241 status = charon->kernel_interface->add_sa(charon->kernel_interface,
242 src, dst,
243 spi, protocols[i],
244 this->reqid,
245 enc_algo, enc_key,
246 int_algo, int_key, mine);
247 /* clean up for next round */
248 if (enc_algo != ENCR_UNDEFINED)
249 {
250 allocator_free_chunk(&enc_key);
251 }
252 if (int_algo != AUTH_UNDEFINED)
253 {
254 allocator_free_chunk(&int_key);
255 }
256
257 if (status != SUCCESS)
258 {
259 return FAILED;
260 }
261
262
263 }
264 }
265 return SUCCESS;
266 }
267
268 static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
269 {
270 linked_list_t *list;
271
272 /* install others (initiators) SAs*/
273 if (install(this, proposal, prf_plus, FALSE) != SUCCESS)
274 {
275 return FAILED;
276 }
277
278 /* get SPIs for our SAs */
279 list = linked_list_create();
280 list->insert_last(list, proposal);
281 if (alloc(this, list) != SUCCESS)
282 {
283 list->destroy(list);
284 return FAILED;
285 }
286 list->destroy(list);
287
288 /* install our (responders) SAs */
289 if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
290 {
291 return FAILED;
292 }
293
294 return SUCCESS;
295 }
296
297 static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
298 {
299 /* install our (initator) SAs */
300 if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
301 {
302 return FAILED;
303 }
304 /* install his (responder) SAs */
305 if (install(this, proposal, prf_plus, FALSE) != SUCCESS)
306 {
307 return FAILED;
308 }
309
310 return SUCCESS;
311 }
312
313 static u_int8_t get_mask(chunk_t start, chunk_t end)
314 {
315 int byte, bit, mask = 0;
316
317 if (start.len != end.len)
318 {
319 return 0;
320 }
321 for (byte = 0; byte < start.len; byte++)
322 {
323 for (bit = 7; bit >= 0; bit--)
324 {
325 if ((*(start.ptr + byte) | (1<<bit)) ==
326 (*(end.ptr + byte) | (1<<bit)))
327 {
328 mask++;
329 }
330 else
331 {
332 return mask;
333 }
334 }
335 }
336 return start.len * 8;
337 }
338
339 static status_t add_policy(private_child_sa_t *this, linked_list_t *my_ts, linked_list_t *other_ts)
340 {
341 traffic_selector_t *local_ts, *remote_ts;
342 host_t *my_net, *other_net;
343 u_int8_t my_mask, other_mask;
344 int family;
345 chunk_t from_addr, to_addr;
346 u_int16_t from_port, to_port;
347 status_t status;
348
349 my_ts->get_first(my_ts, (void**)&local_ts);
350 other_ts->get_first(other_ts, (void**)&remote_ts);
351
352 family = local_ts->get_type(local_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
353 from_addr = local_ts->get_from_address(local_ts);
354 to_addr = local_ts->get_to_address(local_ts);
355 from_port = local_ts->get_from_port(local_ts);
356 to_port = local_ts->get_to_port(local_ts);
357 if (from_port != to_port)
358 {
359 from_port = 0;
360 }
361 my_net = host_create_from_chunk(family, from_addr, from_port);
362 my_mask = get_mask(from_addr, to_addr);
363 allocator_free_chunk(&from_addr);
364 allocator_free_chunk(&to_addr);
365
366 family = remote_ts->get_type(remote_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
367 from_addr = remote_ts->get_from_address(remote_ts);
368 to_addr = remote_ts->get_to_address(remote_ts);
369 from_port = remote_ts->get_from_port(remote_ts);
370 to_port = remote_ts->get_to_port(remote_ts);
371 if (from_port != to_port)
372 {
373 from_port = 0;
374 }
375 other_net = host_create_from_chunk(family, from_addr, from_port);
376 other_mask = get_mask(from_addr, to_addr);
377 allocator_free_chunk(&from_addr);
378 allocator_free_chunk(&to_addr);
379
380 status = charon->kernel_interface->add_policy(charon->kernel_interface,
381 this->me, this->other,
382 my_net, other_net,
383 my_mask, other_mask,
384 XFRM_POLICY_OUT,
385 0, this->ah_spi, this->esp_spi,
386 this->reqid);
387
388 status |= charon->kernel_interface->add_policy(charon->kernel_interface,
389 this->other, this->me,
390 other_net, my_net,
391 other_mask, my_mask,
392 XFRM_POLICY_IN,
393 0, this->ah_spi, this->esp_spi,
394 this->reqid);
395
396 status |= charon->kernel_interface->add_policy(charon->kernel_interface,
397 this->other, this->me,
398 other_net, my_net,
399 other_mask, my_mask,
400 XFRM_POLICY_FWD,
401 0, this->ah_spi, this->esp_spi,
402 this->reqid);
403
404 return status;
405 }
406
407 /**
408 * Implementation of child_sa_t.destroy.
409 */
410 static void destroy(private_child_sa_t *this)
411 {
412 charon->logger_manager->destroy_logger(charon->logger_manager, this->logger);
413 allocator_free(this);
414 }
415
416 /*
417 * Described in header.
418 */
419 child_sa_t * child_sa_create(host_t *me, host_t* other)
420 {
421 static u_int32_t reqid = 123;
422 private_child_sa_t *this = allocator_alloc_thing(private_child_sa_t);
423
424 /* public functions */
425 this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc;
426 this->public.add = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))add;
427 this->public.update = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))update;
428 this->public.add_policy = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policy;
429 this->public.destroy = (void(*)(child_sa_t*))destroy;
430
431 /* private data */
432 this->logger = charon->logger_manager->create_logger(charon->logger_manager, CHILD_SA, NULL);
433 this->me = me;
434 this->other = other;
435 this->ah_spi = 0;
436 this->esp_spi = 0;
437 this->reqid = reqid++;
438
439 return (&this->public);
440 }