]>
Commit | Line | Data |
---|---|---|
c06dbbab MW |
1 | /** |
2 | * @file child_proposal.c | |
3 | * | |
4 | * @brief Implementation of child_proposal_t. | |
5 | * | |
6 | */ | |
7 | ||
8 | /* | |
9 | * Copyright (C) 2006 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_proposal.h" | |
24 | ||
25 | #include <utils/linked_list.h> | |
26 | #include <utils/allocator.h> | |
27 | #include <utils/identification.h> | |
28 | #include <utils/logger.h> | |
29 | ||
30 | ||
31 | /** | |
32 | * String mappings for protocol_id_t. | |
33 | */ | |
34 | mapping_t protocol_id_m[] = { | |
35 | {UNDEFINED_PROTOCOL_ID, "UNDEFINED_PROTOCOL_ID"}, | |
36 | {IKE, "IKE"}, | |
37 | {AH, "AH"}, | |
38 | {ESP, "ESP"}, | |
39 | {MAPPING_END, NULL} | |
40 | }; | |
41 | ||
42 | /** | |
43 | * String mappings for transform_type_t. | |
44 | */ | |
45 | mapping_t transform_type_m[] = { | |
46 | {UNDEFINED_TRANSFORM_TYPE, "UNDEFINED_TRANSFORM_TYPE"}, | |
47 | {ENCRYPTION_ALGORITHM, "ENCRYPTION_ALGORITHM"}, | |
48 | {PSEUDO_RANDOM_FUNCTION, "PSEUDO_RANDOM_FUNCTION"}, | |
49 | {INTEGRITY_ALGORITHM, "INTEGRITY_ALGORITHM"}, | |
50 | {DIFFIE_HELLMAN_GROUP, "DIFFIE_HELLMAN_GROUP"}, | |
51 | {EXTENDED_SEQUENCE_NUMBERS, "EXTENDED_SEQUENCE_NUMBERS"}, | |
52 | {MAPPING_END, NULL} | |
53 | }; | |
54 | ||
55 | /** | |
56 | * String mappings for extended_sequence_numbers_t. | |
57 | */ | |
58 | mapping_t extended_sequence_numbers_m[] = { | |
59 | {NO_EXT_SEQ_NUMBERS, "NO_EXT_SEQ_NUMBERS"}, | |
60 | {EXT_SEQ_NUMBERS, "EXT_SEQ_NUMBERS"}, | |
61 | {MAPPING_END, NULL} | |
62 | }; | |
63 | ||
64 | ||
65 | typedef struct protocol_proposal_t protocol_proposal_t; | |
66 | ||
67 | /** | |
68 | * substructure which holds all data algos for a specific protocol | |
69 | */ | |
70 | struct protocol_proposal_t { | |
71 | /** | |
72 | * protocol (ESP or AH) | |
73 | */ | |
74 | protocol_id_t protocol; | |
75 | ||
76 | /** | |
77 | * priority ordered list of encryption algorithms | |
78 | */ | |
79 | linked_list_t *encryption_algos; | |
80 | ||
81 | /** | |
82 | * priority ordered list of integrity algorithms | |
83 | */ | |
84 | linked_list_t *integrity_algos; | |
85 | ||
86 | /** | |
87 | * priority ordered list of pseudo random functions | |
88 | */ | |
89 | linked_list_t *prf_algos; | |
90 | ||
91 | /** | |
92 | * priority ordered list of dh groups | |
93 | */ | |
94 | linked_list_t *dh_groups; | |
95 | ||
96 | /** | |
97 | * priority ordered list of extended sequence number flags | |
98 | */ | |
99 | linked_list_t *esns; | |
100 | ||
101 | /** | |
102 | * senders SPI | |
103 | */ | |
104 | chunk_t spi; | |
105 | }; | |
106 | ||
107 | ||
108 | typedef struct private_child_proposal_t private_child_proposal_t; | |
109 | ||
110 | /** | |
111 | * Private data of an child_proposal_t object | |
112 | */ | |
113 | struct private_child_proposal_t { | |
114 | ||
115 | /** | |
116 | * Public part | |
117 | */ | |
118 | child_proposal_t public; | |
119 | ||
120 | /** | |
121 | * number of this proposal, as used in the payload | |
122 | */ | |
123 | u_int8_t number; | |
124 | ||
125 | /** | |
126 | * list of protocol_proposal_t's | |
127 | */ | |
128 | linked_list_t *protocol_proposals; | |
129 | }; | |
130 | ||
131 | /** | |
132 | * Look up a protocol_proposal, or create one if necessary... | |
133 | */ | |
134 | static protocol_proposal_t *get_protocol_proposal(private_child_proposal_t *this, protocol_id_t proto, bool create) | |
135 | { | |
136 | protocol_proposal_t *proto_proposal = NULL, *current_proto_proposal;; | |
137 | iterator_t *iterator; | |
138 | ||
139 | /* find our protocol in the proposals */ | |
140 | iterator = this->protocol_proposals->create_iterator(this->protocol_proposals, TRUE); | |
141 | while (iterator->has_next(iterator)) | |
142 | { | |
143 | iterator->current(iterator, (void**)¤t_proto_proposal); | |
144 | if (current_proto_proposal->protocol == proto) | |
145 | { | |
146 | proto_proposal = current_proto_proposal; | |
147 | break; | |
148 | } | |
149 | } | |
150 | iterator->destroy(iterator); | |
151 | ||
152 | if (!proto_proposal && create) | |
153 | { | |
154 | /* nope, create a new one */ | |
155 | proto_proposal = allocator_alloc_thing(protocol_proposal_t); | |
156 | proto_proposal->protocol = proto; | |
157 | proto_proposal->encryption_algos = linked_list_create(); | |
158 | proto_proposal->integrity_algos = linked_list_create(); | |
159 | proto_proposal->prf_algos = linked_list_create(); | |
160 | proto_proposal->dh_groups = linked_list_create(); | |
161 | proto_proposal->esns = linked_list_create(); | |
162 | if (proto == IKE) | |
163 | { | |
164 | proto_proposal->spi.len = 8; | |
165 | } | |
166 | else | |
167 | { | |
168 | proto_proposal->spi.len = 4; | |
169 | } | |
170 | proto_proposal->spi.ptr = allocator_alloc(proto_proposal->spi.len); | |
171 | /* add to the list */ | |
172 | this->protocol_proposals->insert_last(this->protocol_proposals, (void*)proto_proposal); | |
173 | } | |
174 | return proto_proposal; | |
175 | } | |
176 | ||
177 | /** | |
178 | * Add algorithm/keysize to a algorithm list | |
179 | */ | |
180 | static void add_algo(linked_list_t *list, u_int8_t algo, size_t key_size) | |
181 | { | |
182 | algorithm_t *algo_key = allocator_alloc_thing(algorithm_t); | |
183 | ||
184 | algo_key->algorithm = algo; | |
185 | algo_key->key_size = key_size; | |
186 | list->insert_last(list, (void*)algo_key); | |
187 | } | |
188 | ||
189 | /** | |
190 | * Implements child_proposal_t.add_algorithm | |
191 | */ | |
192 | static void add_algorithm(private_child_proposal_t *this, protocol_id_t proto, transform_type_t type, u_int16_t algo, size_t key_size) | |
193 | { | |
194 | protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, TRUE); | |
195 | ||
196 | switch (type) | |
197 | { | |
198 | case ENCRYPTION_ALGORITHM: | |
199 | add_algo(proto_proposal->encryption_algos, algo, key_size); | |
200 | break; | |
201 | case INTEGRITY_ALGORITHM: | |
202 | add_algo(proto_proposal->integrity_algos, algo, key_size); | |
203 | break; | |
204 | case PSEUDO_RANDOM_FUNCTION: | |
205 | add_algo(proto_proposal->prf_algos, algo, key_size); | |
206 | break; | |
207 | case DIFFIE_HELLMAN_GROUP: | |
208 | add_algo(proto_proposal->dh_groups, algo, 0); | |
209 | break; | |
210 | case EXTENDED_SEQUENCE_NUMBERS: | |
211 | add_algo(proto_proposal->esns, algo, 0); | |
212 | break; | |
213 | default: | |
214 | break; | |
215 | } | |
216 | } | |
217 | ||
218 | /** | |
219 | * Implements child_proposal_t.create_algorithm_iterator. | |
220 | */ | |
221 | static iterator_t *create_algorithm_iterator(private_child_proposal_t *this, protocol_id_t proto, transform_type_t type) | |
222 | { | |
223 | protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, FALSE); | |
224 | if (proto_proposal == NULL) | |
225 | { | |
226 | return NULL; | |
227 | } | |
228 | ||
229 | switch (type) | |
230 | { | |
231 | case ENCRYPTION_ALGORITHM: | |
232 | return proto_proposal->encryption_algos->create_iterator(proto_proposal->encryption_algos, TRUE); | |
233 | case INTEGRITY_ALGORITHM: | |
234 | return proto_proposal->integrity_algos->create_iterator(proto_proposal->integrity_algos, TRUE); | |
235 | case PSEUDO_RANDOM_FUNCTION: | |
236 | return proto_proposal->prf_algos->create_iterator(proto_proposal->prf_algos, TRUE); | |
237 | case DIFFIE_HELLMAN_GROUP: | |
238 | return proto_proposal->dh_groups->create_iterator(proto_proposal->dh_groups, TRUE); | |
239 | case EXTENDED_SEQUENCE_NUMBERS: | |
240 | return proto_proposal->esns->create_iterator(proto_proposal->esns, TRUE); | |
241 | default: | |
242 | break; | |
243 | } | |
244 | return NULL; | |
245 | ||
246 | } | |
247 | ||
248 | /** | |
249 | * Find a matching alg/keysize in two linked lists | |
250 | */ | |
251 | static bool select_algo(linked_list_t *first, linked_list_t *second, u_int16_t *alg, size_t *key_size) | |
252 | { | |
253 | iterator_t *first_iter, *second_iter; | |
254 | algorithm_t *first_alg, *second_alg; | |
255 | ||
256 | /* if in both are zero algorithms specified, we HAVE a match */ | |
257 | if (first->get_count(first) == 0 && second->get_count(second) == 0) | |
258 | { | |
259 | *alg = 0; | |
260 | return TRUE; | |
261 | } | |
262 | ||
263 | first_iter = first->create_iterator(first, TRUE); | |
264 | second_iter = second->create_iterator(second, TRUE); | |
265 | /* compare algs, order of algs in "first" is preferred */ | |
266 | while (first_iter->has_next(first_iter)) | |
267 | { | |
268 | first_iter->current(first_iter, (void**)&first_alg); | |
269 | second_iter->reset(second_iter); | |
270 | while (second_iter->has_next(second_iter)) | |
271 | { | |
272 | second_iter->current(second_iter, (void**)&second_alg); | |
273 | if (first_alg->algorithm == second_alg->algorithm && | |
274 | first_alg->key_size == second_alg->key_size) | |
275 | { | |
276 | /* ok, we have an algorithm */ | |
277 | *alg = first_alg->algorithm; | |
278 | *key_size = first_alg->key_size; | |
279 | first_iter->destroy(first_iter); | |
280 | second_iter->destroy(second_iter); | |
281 | return TRUE; | |
282 | } | |
283 | } | |
284 | } | |
285 | /* no match in all comparisons */ | |
286 | first_iter->destroy(first_iter); | |
287 | second_iter->destroy(second_iter); | |
288 | return FALSE; | |
289 | } | |
290 | ||
291 | /** | |
292 | * Implements child_proposal_t.select. | |
293 | */ | |
294 | static child_proposal_t *select_proposal(private_child_proposal_t *this, private_child_proposal_t *other) | |
295 | { | |
296 | child_proposal_t *selected; | |
297 | u_int16_t algo; | |
298 | size_t key_size; | |
299 | iterator_t *iterator; | |
300 | protocol_proposal_t *this_prop, *other_prop; | |
301 | protocol_id_t proto; | |
302 | ||
303 | /* empty proposal? no match */ | |
304 | if (this->protocol_proposals->get_count(this->protocol_proposals) == 0 || | |
305 | other->protocol_proposals->get_count(other->protocol_proposals) == 0) | |
306 | { | |
307 | return NULL; | |
308 | } | |
309 | /* they MUST have the same amount of protocols */ | |
310 | if (this->protocol_proposals->get_count(this->protocol_proposals) != | |
311 | other->protocol_proposals->get_count(other->protocol_proposals)) | |
312 | { | |
313 | return NULL; | |
314 | } | |
315 | ||
316 | selected = child_proposal_create(this->number); | |
317 | ||
318 | /* iterate over supplied proposals */ | |
319 | iterator = other->protocol_proposals->create_iterator(other->protocol_proposals, TRUE); | |
320 | while (iterator->has_next(iterator)) | |
321 | { | |
322 | iterator->current(iterator, (void**)&other_prop); | |
323 | /* get the proposal with the same protocol */ | |
324 | proto = other_prop->protocol; | |
325 | this_prop = get_protocol_proposal(this, proto, FALSE); | |
326 | ||
327 | if (this_prop == NULL) | |
328 | { | |
329 | iterator->destroy(iterator); | |
330 | selected->destroy(selected); | |
331 | return NULL; | |
332 | } | |
333 | ||
334 | /* select encryption algorithm */ | |
335 | if (select_algo(this_prop->encryption_algos, other_prop->encryption_algos, &algo, &key_size)) | |
336 | { | |
337 | if (algo) | |
338 | { | |
339 | selected->add_algorithm(selected, proto, ENCRYPTION_ALGORITHM, algo, key_size); | |
340 | } | |
341 | } | |
342 | else | |
343 | { | |
344 | iterator->destroy(iterator); | |
345 | selected->destroy(selected); | |
346 | return NULL; | |
347 | } | |
348 | /* select integrity algorithm */ | |
349 | if (select_algo(this_prop->integrity_algos, other_prop->integrity_algos, &algo, &key_size)) | |
350 | { | |
351 | if (algo) | |
352 | { | |
353 | selected->add_algorithm(selected, proto, INTEGRITY_ALGORITHM, algo, key_size); | |
354 | } | |
355 | } | |
356 | else | |
357 | { | |
358 | iterator->destroy(iterator); | |
359 | selected->destroy(selected); | |
360 | return NULL; | |
361 | } | |
362 | /* select prf algorithm */ | |
363 | if (select_algo(this_prop->prf_algos, other_prop->prf_algos, &algo, &key_size)) | |
364 | { | |
365 | if (algo) | |
366 | { | |
367 | selected->add_algorithm(selected, proto, PSEUDO_RANDOM_FUNCTION, algo, key_size); | |
368 | } | |
369 | } | |
370 | else | |
371 | { | |
372 | iterator->destroy(iterator); | |
373 | selected->destroy(selected); | |
374 | return NULL; | |
375 | } | |
376 | /* select a DH-group */ | |
377 | if (select_algo(this_prop->dh_groups, other_prop->dh_groups, &algo, &key_size)) | |
378 | { | |
379 | if (algo) | |
380 | { | |
381 | selected->add_algorithm(selected, proto, DIFFIE_HELLMAN_GROUP, algo, 0); | |
382 | } | |
383 | } | |
384 | else | |
385 | { | |
386 | iterator->destroy(iterator); | |
387 | selected->destroy(selected); | |
388 | return NULL; | |
389 | } | |
390 | /* select if we use ESNs */ | |
391 | if (select_algo(this_prop->esns, other_prop->esns, &algo, &key_size)) | |
392 | { | |
393 | if (algo) | |
394 | { | |
395 | selected->add_algorithm(selected, proto, EXTENDED_SEQUENCE_NUMBERS, algo, 0); | |
396 | } | |
397 | } | |
398 | else | |
399 | { | |
400 | iterator->destroy(iterator); | |
401 | selected->destroy(selected); | |
402 | return NULL; | |
403 | } | |
404 | } | |
405 | iterator->destroy(iterator); | |
406 | /* everything matched, return new proposal */ | |
407 | return selected; | |
408 | } | |
409 | ||
410 | /** | |
411 | * Implements child_proposal_t.get_number. | |
412 | */ | |
413 | static u_int8_t get_number(private_child_proposal_t *this) | |
414 | { | |
415 | return this->number; | |
416 | } | |
417 | ||
418 | /** | |
419 | * Implements child_proposal_t.get_protocols. | |
420 | */ | |
421 | static void get_protocols(private_child_proposal_t *this, protocol_id_t ids[2]) | |
422 | { | |
423 | iterator_t *iterator = this->protocol_proposals->create_iterator(this->protocol_proposals, TRUE); | |
424 | u_int i = 0; | |
425 | ||
426 | ids[0] = UNDEFINED_PROTOCOL_ID; | |
427 | ids[1] = UNDEFINED_PROTOCOL_ID; | |
428 | while (iterator->has_next(iterator)) | |
429 | { | |
430 | protocol_proposal_t *proto_prop; | |
431 | iterator->current(iterator, (void**)&proto_prop); | |
432 | ids[i++] = proto_prop->protocol; | |
433 | if (i>1) | |
434 | { | |
435 | /* should not happen, but who knows */ | |
436 | return; | |
437 | } | |
438 | } | |
439 | } | |
440 | ||
441 | /** | |
442 | * Implements child_proposal_t.set_spi. | |
443 | */ | |
444 | static void set_spi(private_child_proposal_t *this, protocol_id_t proto, u_int64_t spi) | |
445 | { | |
446 | protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, FALSE); | |
447 | if (proto_proposal) | |
448 | { | |
449 | if (proto == IKE) | |
450 | { | |
451 | *((u_int32_t*)proto_proposal->spi.ptr) = (u_int32_t)spi; | |
452 | } | |
453 | else | |
454 | { | |
455 | *((u_int64_t*)proto_proposal->spi.ptr) = spi; | |
456 | } | |
457 | ||
458 | } | |
459 | } | |
460 | ||
461 | /** | |
462 | * Implements child_proposal_t.get_spi. | |
463 | */ | |
464 | static u_int64_t get_spi(private_child_proposal_t *this, protocol_id_t proto) | |
465 | { | |
466 | protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, FALSE); | |
467 | if (proto_proposal) | |
468 | { | |
469 | if (proto == IKE) | |
470 | { | |
471 | return (u_int64_t)*((u_int32_t*)proto_proposal->spi.ptr); | |
472 | } | |
473 | else | |
474 | { | |
475 | return *((u_int64_t*)proto_proposal->spi.ptr); | |
476 | } | |
477 | } | |
478 | return 0; | |
479 | } | |
480 | ||
481 | /** | |
482 | * Frees all list items and destroys the list | |
483 | */ | |
484 | static void free_algo_list(linked_list_t *list) | |
485 | { | |
486 | algorithm_t *algo; | |
487 | ||
488 | while(list->get_count(list) > 0) | |
489 | { | |
490 | list->remove_last(list, (void**)&algo); | |
491 | allocator_free(algo); | |
492 | } | |
493 | list->destroy(list); | |
494 | } | |
495 | ||
496 | /** | |
497 | * Implements child_proposal_t.destroy. | |
498 | */ | |
499 | static void destroy(private_child_proposal_t *this) | |
500 | { | |
501 | while(this->protocol_proposals->get_count(this->protocol_proposals) > 0) | |
502 | { | |
503 | protocol_proposal_t *proto_prop; | |
504 | this->protocol_proposals->remove_last(this->protocol_proposals, (void**)&proto_prop); | |
505 | ||
506 | free_algo_list(proto_prop->encryption_algos); | |
507 | free_algo_list(proto_prop->integrity_algos); | |
508 | free_algo_list(proto_prop->prf_algos); | |
509 | free_algo_list(proto_prop->dh_groups); | |
510 | free_algo_list(proto_prop->esns); | |
511 | ||
512 | allocator_free(proto_prop->spi.ptr); | |
513 | allocator_free(proto_prop); | |
514 | } | |
515 | this->protocol_proposals->destroy(this->protocol_proposals); | |
516 | ||
517 | allocator_free(this); | |
518 | } | |
519 | ||
520 | /* | |
521 | * Describtion in header-file | |
522 | */ | |
523 | child_proposal_t *child_proposal_create(u_int8_t number) | |
524 | { | |
525 | private_child_proposal_t *this = allocator_alloc_thing(private_child_proposal_t); | |
526 | ||
527 | this->public.add_algorithm = (void (*)(child_proposal_t*,protocol_id_t,transform_type_t,u_int16_t,size_t))add_algorithm; | |
528 | this->public.create_algorithm_iterator = (iterator_t* (*)(child_proposal_t*,protocol_id_t,transform_type_t))create_algorithm_iterator; | |
529 | this->public.select = (child_proposal_t* (*)(child_proposal_t*,child_proposal_t*))select_proposal; | |
530 | this->public.get_number = (u_int8_t (*)(child_proposal_t*))get_number; | |
531 | this->public.get_protocols = (void(*)(child_proposal_t *this, protocol_id_t ids[2]))get_protocols; | |
532 | this->public.set_spi = (void(*)(child_proposal_t*,protocol_id_t,u_int64_t spi))set_spi; | |
533 | this->public.get_spi = (u_int64_t(*)(child_proposal_t*,protocol_id_t))get_spi; | |
534 | this->public.destroy = (void(*)(child_proposal_t*))destroy; | |
535 | ||
536 | /* init private members*/ | |
537 | this->number = number; | |
538 | this->protocol_proposals = linked_list_create(); | |
539 | ||
540 | return (&this->public); | |
541 | } |