Properly filter IKEv1 proposals consisting of multiple proposal payloads.
[strongswan.git] / src / libcharon / encoding / payloads / sa_payload.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2005-2010 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include <stddef.h>
19
20 #include "sa_payload.h"
21
22 #include <encoding/payloads/encodings.h>
23 #include <utils/linked_list.h>
24 #include <daemon.h>
25
26 /* IKEv1 situation */
27 #define SIT_IDENTITY_ONLY 1
28
29 typedef struct private_sa_payload_t private_sa_payload_t;
30
31 /**
32 * Private data of an sa_payload_t object.
33 */
34 struct private_sa_payload_t {
35
36 /**
37 * Public sa_payload_t interface.
38 */
39 sa_payload_t public;
40
41 /**
42 * Next payload type.
43 */
44 u_int8_t next_payload;
45
46 /**
47 * Critical flag.
48 */
49 bool critical;
50
51 /**
52 * Reserved bits
53 */
54 bool reserved[8];
55
56 /**
57 * Length of this payload.
58 */
59 u_int16_t payload_length;
60
61 /**
62 * Proposals in this payload are stored in a linked_list_t.
63 */
64 linked_list_t *proposals;
65
66 /**
67 * Type of this payload, V1 or V2
68 */
69 payload_type_t type;
70
71 /**
72 * IKEv1 DOI
73 */
74 u_int32_t doi;
75
76 /**
77 * IKEv1 situation
78 */
79 u_int32_t situation;
80 };
81
82 /**
83 * Encoding rules for IKEv1 SA payload
84 */
85 static encoding_rule_t encodings_v1[] = {
86 /* 1 Byte next payload type, stored in the field next_payload */
87 { U_INT_8, offsetof(private_sa_payload_t, next_payload) },
88 /* 8 reserved bits */
89 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[0]) },
90 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[1]) },
91 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[2]) },
92 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[3]) },
93 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[4]) },
94 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[5]) },
95 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[6]) },
96 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[7]) },
97 /* Length of the whole SA payload*/
98 { PAYLOAD_LENGTH, offsetof(private_sa_payload_t, payload_length) },
99 /* DOI*/
100 { U_INT_32, offsetof(private_sa_payload_t, doi) },
101 /* Situation*/
102 { U_INT_32, offsetof(private_sa_payload_t, situation) },
103 /* Proposals are stored in a proposal substructure list */
104 { PAYLOAD_LIST + PROPOSAL_SUBSTRUCTURE_V1,
105 offsetof(private_sa_payload_t, proposals) },
106 };
107
108 /*
109 1 2 3
110 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
111 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
112 ! Next Payload ! RESERVED ! Payload Length !
113 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
114 ! DOI !
115 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
116 ! Situation !
117 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
118 ! !
119 ~ <Proposals> ~
120 ! !
121 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
122 */
123
124 /**
125 * Encoding rules for IKEv2 SA payload
126 */
127 static encoding_rule_t encodings_v2[] = {
128 /* 1 Byte next payload type, stored in the field next_payload */
129 { U_INT_8, offsetof(private_sa_payload_t, next_payload) },
130 /* the critical bit */
131 { FLAG, offsetof(private_sa_payload_t, critical) },
132 /* 7 Bit reserved bits */
133 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[0]) },
134 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[1]) },
135 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[2]) },
136 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[3]) },
137 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[4]) },
138 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[5]) },
139 { RESERVED_BIT, offsetof(private_sa_payload_t, reserved[6]) },
140 /* Length of the whole SA payload*/
141 { PAYLOAD_LENGTH, offsetof(private_sa_payload_t, payload_length) },
142 /* Proposals are stored in a proposal substructure list */
143 { PAYLOAD_LIST + PROPOSAL_SUBSTRUCTURE,
144 offsetof(private_sa_payload_t, proposals) },
145 };
146
147 /*
148 1 2 3
149 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
150 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
151 ! Next Payload !C! RESERVED ! Payload Length !
152 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
153 ! !
154 ~ <Proposals> ~
155 ! !
156 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
157 */
158
159 METHOD(payload_t, verify, status_t,
160 private_sa_payload_t *this)
161 {
162 int expected_number = 0, current_number;
163 status_t status = SUCCESS;
164 enumerator_t *enumerator;
165 proposal_substructure_t *substruct;
166
167 if (this->type == SECURITY_ASSOCIATION)
168 {
169 expected_number = 1;
170 }
171
172 /* check proposal numbering */
173 enumerator = this->proposals->create_enumerator(this->proposals);
174 while (enumerator->enumerate(enumerator, (void**)&substruct))
175 {
176 current_number = substruct->get_proposal_number(substruct);
177 if (current_number < expected_number)
178 {
179 DBG1(DBG_ENC, "proposal number smaller than previous");
180 status = FAILED;
181 break;
182 }
183
184 status = substruct->payload_interface.verify(&substruct->payload_interface);
185 if (status != SUCCESS)
186 {
187 DBG1(DBG_ENC, "PROPOSAL_SUBSTRUCTURE verification failed");
188 break;
189 }
190 expected_number = current_number;
191 }
192 enumerator->destroy(enumerator);
193 return status;
194 }
195
196 METHOD(payload_t, get_encoding_rules, int,
197 private_sa_payload_t *this, encoding_rule_t **rules)
198 {
199 if (this->type == SECURITY_ASSOCIATION_V1)
200 {
201 *rules = encodings_v1;
202 return countof(encodings_v1);
203 }
204 *rules = encodings_v2;
205 return countof(encodings_v2);
206 }
207
208 METHOD(payload_t, get_header_length, int,
209 private_sa_payload_t *this)
210 {
211 if (this->type == SECURITY_ASSOCIATION_V1)
212 {
213 return 12;
214 }
215 return 4;
216 }
217
218 METHOD(payload_t, get_type, payload_type_t,
219 private_sa_payload_t *this)
220 {
221 return this->type;
222 }
223
224 METHOD(payload_t, get_next_type, payload_type_t,
225 private_sa_payload_t *this)
226 {
227 return this->next_payload;
228 }
229
230 METHOD(payload_t, set_next_type, void,
231 private_sa_payload_t *this,payload_type_t type)
232 {
233 this->next_payload = type;
234 }
235
236 /**
237 * recompute length of the payload.
238 */
239 static void compute_length(private_sa_payload_t *this)
240 {
241 enumerator_t *enumerator;
242 payload_t *current;
243
244 this->payload_length = get_header_length(this);
245
246 enumerator = this->proposals->create_enumerator(this->proposals);
247 while (enumerator->enumerate(enumerator, (void **)&current))
248 {
249 this->payload_length += current->get_length(current);
250 }
251 enumerator->destroy(enumerator);
252 }
253
254 METHOD(payload_t, get_length, size_t,
255 private_sa_payload_t *this)
256 {
257 return this->payload_length;
258 }
259
260 /**
261 * Create a transform substructure from a proposal, add to payload
262 */
263 static void add_proposal_v2(private_sa_payload_t *this, proposal_t *proposal)
264 {
265 proposal_substructure_t *substruct, *last;
266 u_int count;
267
268 substruct = proposal_substructure_create_from_proposal_v2(proposal);
269 count = this->proposals->get_count(this->proposals);
270 if (count > 0)
271 {
272 this->proposals->get_last(this->proposals, (void**)&last);
273 /* last transform is now not anymore last one */
274 last->set_is_last_proposal(last, FALSE);
275 }
276 substruct->set_is_last_proposal(substruct, TRUE);
277 if (proposal->get_number(proposal))
278 { /* use the selected proposals number, if any */
279 substruct->set_proposal_number(substruct, proposal->get_number(proposal));
280 }
281 else
282 {
283 substruct->set_proposal_number(substruct, count + 1);
284 }
285 this->proposals->insert_last(this->proposals, substruct);
286 compute_length(this);
287 }
288
289 METHOD(sa_payload_t, get_proposals, linked_list_t*,
290 private_sa_payload_t *this)
291 {
292 int struct_number = 0;
293 int ignore_struct_number = 0;
294 enumerator_t *enumerator;
295 proposal_substructure_t *substruct;
296 linked_list_t *substructs, *list;
297
298 if (this->type == SECURITY_ASSOCIATION_V1)
299 { /* IKEv1 proposals start with 0 */
300 struct_number = ignore_struct_number = -1;
301 }
302
303 /* we do not support proposals split up to two proposal substructures, as
304 * AH+ESP bundles are not supported in RFC4301 anymore.
305 * To handle such structures safely, we just skip proposals with multiple
306 * protocols.
307 */
308 substructs = linked_list_create();
309 enumerator = this->proposals->create_enumerator(this->proposals);
310 while (enumerator->enumerate(enumerator, &substruct))
311 {
312 /* check if a proposal has a single protocol */
313 if (substruct->get_proposal_number(substruct) == struct_number)
314 {
315 if (ignore_struct_number < struct_number)
316 { /* remove an already added, if first of series */
317 substructs->remove_last(substructs, (void**)&substruct);
318 ignore_struct_number = struct_number;
319 }
320 continue;
321 }
322 struct_number++;
323 substructs->insert_last(substructs, substruct);
324 }
325 enumerator->destroy(enumerator);
326
327 /* generate proposals from substructs */
328 list = linked_list_create();
329 enumerator = substructs->create_enumerator(substructs);
330 while (enumerator->enumerate(enumerator, &substruct))
331 {
332 substruct->get_proposals(substruct, list);
333 }
334 enumerator->destroy(enumerator);
335 substructs->destroy(substructs);
336 return list;
337 }
338
339 METHOD(sa_payload_t, create_substructure_enumerator, enumerator_t*,
340 private_sa_payload_t *this)
341 {
342 return this->proposals->create_enumerator(this->proposals);
343 }
344
345 METHOD(sa_payload_t, get_lifetime, u_int32_t,
346 private_sa_payload_t *this)
347 {
348 proposal_substructure_t *substruct;
349 enumerator_t *enumerator;
350 u_int32_t lifetime = 0;
351
352 enumerator = this->proposals->create_enumerator(this->proposals);
353 if (enumerator->enumerate(enumerator, &substruct))
354 {
355 lifetime = substruct->get_lifetime(substruct);
356 }
357 enumerator->destroy(enumerator);
358
359 return lifetime;
360 }
361
362 METHOD(sa_payload_t, get_lifebytes, u_int64_t,
363 private_sa_payload_t *this)
364 {
365 proposal_substructure_t *substruct;
366 enumerator_t *enumerator;
367 u_int64_t lifebytes = 0;
368
369 enumerator = this->proposals->create_enumerator(this->proposals);
370 if (enumerator->enumerate(enumerator, &substruct))
371 {
372 lifebytes = substruct->get_lifebytes(substruct);
373 }
374 enumerator->destroy(enumerator);
375
376 return lifebytes;
377 }
378
379 METHOD(sa_payload_t, get_auth_method, auth_method_t,
380 private_sa_payload_t *this)
381 {
382 proposal_substructure_t *substruct;
383 enumerator_t *enumerator;
384 auth_method_t method = AUTH_NONE;
385
386 enumerator = this->proposals->create_enumerator(this->proposals);
387 if (enumerator->enumerate(enumerator, &substruct))
388 {
389 method = substruct->get_auth_method(substruct);
390 }
391 enumerator->destroy(enumerator);
392
393 return method;
394 }
395
396 METHOD(sa_payload_t, get_encap_mode, ipsec_mode_t,
397 private_sa_payload_t *this, bool *udp)
398 {
399 proposal_substructure_t *substruct;
400 enumerator_t *enumerator;
401 ipsec_mode_t mode = MODE_NONE;
402
403 enumerator = this->proposals->create_enumerator(this->proposals);
404 if (enumerator->enumerate(enumerator, &substruct))
405 {
406 mode = substruct->get_encap_mode(substruct, udp);
407 }
408 enumerator->destroy(enumerator);
409
410 return mode;
411 }
412
413 METHOD2(payload_t, sa_payload_t, destroy, void,
414 private_sa_payload_t *this)
415 {
416 this->proposals->destroy_offset(this->proposals,
417 offsetof(payload_t, destroy));
418 free(this);
419 }
420
421 /*
422 * Described in header.
423 */
424 sa_payload_t *sa_payload_create(payload_type_t type)
425 {
426 private_sa_payload_t *this;
427
428 INIT(this,
429 .public = {
430 .payload_interface = {
431 .verify = _verify,
432 .get_encoding_rules = _get_encoding_rules,
433 .get_header_length = _get_header_length,
434 .get_length = _get_length,
435 .get_next_type = _get_next_type,
436 .set_next_type = _set_next_type,
437 .get_type = _get_type,
438 .destroy = _destroy,
439 },
440 .get_proposals = _get_proposals,
441 .create_substructure_enumerator = _create_substructure_enumerator,
442 .get_lifetime = _get_lifetime,
443 .get_lifebytes = _get_lifebytes,
444 .get_auth_method = _get_auth_method,
445 .get_encap_mode = _get_encap_mode,
446 .destroy = _destroy,
447 },
448 .next_payload = NO_PAYLOAD,
449 .proposals = linked_list_create(),
450 .type = type,
451 /* for IKEv1 only */
452 .doi = IKEV1_DOI_IPSEC,
453 .situation = SIT_IDENTITY_ONLY,
454 );
455
456 compute_length(this);
457
458 return &this->public;
459 }
460
461 /*
462 * Described in header.
463 */
464 sa_payload_t *sa_payload_create_from_proposals_v2(linked_list_t *proposals)
465 {
466 private_sa_payload_t *this;
467 enumerator_t *enumerator;
468 proposal_t *proposal;
469
470 this = (private_sa_payload_t*)sa_payload_create(SECURITY_ASSOCIATION);
471 enumerator = proposals->create_enumerator(proposals);
472 while (enumerator->enumerate(enumerator, &proposal))
473 {
474 add_proposal_v2(this, proposal);
475 }
476 enumerator->destroy(enumerator);
477
478 return &this->public;
479 }
480
481 /*
482 * Described in header.
483 */
484 sa_payload_t *sa_payload_create_from_proposal_v2(proposal_t *proposal)
485 {
486 private_sa_payload_t *this;
487
488 this = (private_sa_payload_t*)sa_payload_create(SECURITY_ASSOCIATION);
489 add_proposal_v2(this, proposal);
490
491 return &this->public;
492
493 }
494
495 /*
496 * Described in header.
497 */
498 sa_payload_t *sa_payload_create_from_proposals_v1(linked_list_t *proposals,
499 u_int32_t lifetime, u_int64_t lifebytes,
500 auth_method_t auth, ipsec_mode_t mode, bool udp)
501 {
502 proposal_substructure_t *substruct;
503 private_sa_payload_t *this;
504
505 this = (private_sa_payload_t*)sa_payload_create(SECURITY_ASSOCIATION_V1);
506
507 /* IKEv1 encodes multiple proposals in a single substructure
508 * TODO-IKEv1: Encode ESP+AH proposals in two different substructs */
509 substruct = proposal_substructure_create_from_proposals_v1(proposals,
510 lifetime, lifebytes, auth, mode, udp);
511 substruct->set_is_last_proposal(substruct, TRUE);
512 this->proposals->insert_last(this->proposals, substruct);
513 compute_length(this);
514
515 return &this->public;
516 }
517
518 /*
519 * Described in header.
520 */
521 sa_payload_t *sa_payload_create_from_proposal_v1(proposal_t *proposal,
522 u_int32_t lifetime, u_int64_t lifebytes,
523 auth_method_t auth, ipsec_mode_t mode, bool udp)
524 {
525 proposal_substructure_t *substruct;
526 private_sa_payload_t *this;
527
528 this = (private_sa_payload_t*)sa_payload_create(SECURITY_ASSOCIATION_V1);
529
530 substruct = proposal_substructure_create_from_proposal_v1(proposal,
531 lifetime, lifebytes, auth, mode, udp);
532 substruct->set_is_last_proposal(substruct, TRUE);
533 this->proposals->insert_last(this->proposals, substruct);
534 compute_length(this);
535
536 return &this->public;
537 }