Add an additional proposal without IPComp to SA payload.
[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, get_ipcomp_proposals, linked_list_t*,
340 private_sa_payload_t *this, u_int16_t *cpi)
341 {
342 int current_proposal = -1, unsupported_proposal = -1;
343 enumerator_t *enumerator;
344 proposal_substructure_t *substruct, *esp = NULL, *ipcomp = NULL;
345 linked_list_t *list;
346
347 /* we currently only support the combination ESP+IPComp, find the first */
348 enumerator = this->proposals->create_enumerator(this->proposals);
349 while (enumerator->enumerate(enumerator, &substruct))
350 {
351 u_int8_t proposal_number = substruct->get_proposal_number(substruct);
352 u_int8_t protocol_id = substruct->get_protocol_id(substruct);
353
354 if (proposal_number == unsupported_proposal)
355 {
356 continue;
357 }
358 if (protocol_id != PROTO_ESP && protocol_id != PROTO_IPCOMP)
359 { /* unsupported combination */
360 esp = ipcomp = NULL;
361 unsupported_proposal = current_proposal;
362 continue;
363 }
364 if (proposal_number != current_proposal)
365 { /* start of a new proposal */
366 if (esp && ipcomp)
367 { /* previous proposal is valid */
368 break;
369 }
370 esp = ipcomp = NULL;
371 current_proposal = proposal_number;
372 }
373 switch (protocol_id)
374 {
375 case PROTO_ESP:
376 esp = substruct;
377 break;
378 case PROTO_IPCOMP:
379 ipcomp = substruct;
380 break;
381 }
382 }
383 enumerator->destroy(enumerator);
384
385 list = linked_list_create();
386 if (esp && ipcomp && ipcomp->get_cpi(ipcomp, cpi))
387 {
388 esp->get_proposals(esp, list);
389 }
390 return list;
391 }
392
393 METHOD(sa_payload_t, create_substructure_enumerator, enumerator_t*,
394 private_sa_payload_t *this)
395 {
396 return this->proposals->create_enumerator(this->proposals);
397 }
398
399 METHOD(sa_payload_t, get_lifetime, u_int32_t,
400 private_sa_payload_t *this)
401 {
402 proposal_substructure_t *substruct;
403 enumerator_t *enumerator;
404 u_int32_t lifetime = 0;
405
406 enumerator = this->proposals->create_enumerator(this->proposals);
407 if (enumerator->enumerate(enumerator, &substruct))
408 {
409 lifetime = substruct->get_lifetime(substruct);
410 }
411 enumerator->destroy(enumerator);
412
413 return lifetime;
414 }
415
416 METHOD(sa_payload_t, get_lifebytes, u_int64_t,
417 private_sa_payload_t *this)
418 {
419 proposal_substructure_t *substruct;
420 enumerator_t *enumerator;
421 u_int64_t lifebytes = 0;
422
423 enumerator = this->proposals->create_enumerator(this->proposals);
424 if (enumerator->enumerate(enumerator, &substruct))
425 {
426 lifebytes = substruct->get_lifebytes(substruct);
427 }
428 enumerator->destroy(enumerator);
429
430 return lifebytes;
431 }
432
433 METHOD(sa_payload_t, get_auth_method, auth_method_t,
434 private_sa_payload_t *this)
435 {
436 proposal_substructure_t *substruct;
437 enumerator_t *enumerator;
438 auth_method_t method = AUTH_NONE;
439
440 enumerator = this->proposals->create_enumerator(this->proposals);
441 if (enumerator->enumerate(enumerator, &substruct))
442 {
443 method = substruct->get_auth_method(substruct);
444 }
445 enumerator->destroy(enumerator);
446
447 return method;
448 }
449
450 METHOD(sa_payload_t, get_encap_mode, ipsec_mode_t,
451 private_sa_payload_t *this, bool *udp)
452 {
453 proposal_substructure_t *substruct;
454 enumerator_t *enumerator;
455 ipsec_mode_t mode = MODE_NONE;
456
457 enumerator = this->proposals->create_enumerator(this->proposals);
458 if (enumerator->enumerate(enumerator, &substruct))
459 {
460 mode = substruct->get_encap_mode(substruct, udp);
461 }
462 enumerator->destroy(enumerator);
463
464 return mode;
465 }
466
467 METHOD2(payload_t, sa_payload_t, destroy, void,
468 private_sa_payload_t *this)
469 {
470 this->proposals->destroy_offset(this->proposals,
471 offsetof(payload_t, destroy));
472 free(this);
473 }
474
475 /*
476 * Described in header.
477 */
478 sa_payload_t *sa_payload_create(payload_type_t type)
479 {
480 private_sa_payload_t *this;
481
482 INIT(this,
483 .public = {
484 .payload_interface = {
485 .verify = _verify,
486 .get_encoding_rules = _get_encoding_rules,
487 .get_header_length = _get_header_length,
488 .get_length = _get_length,
489 .get_next_type = _get_next_type,
490 .set_next_type = _set_next_type,
491 .get_type = _get_type,
492 .destroy = _destroy,
493 },
494 .get_proposals = _get_proposals,
495 .get_ipcomp_proposals = _get_ipcomp_proposals,
496 .create_substructure_enumerator = _create_substructure_enumerator,
497 .get_lifetime = _get_lifetime,
498 .get_lifebytes = _get_lifebytes,
499 .get_auth_method = _get_auth_method,
500 .get_encap_mode = _get_encap_mode,
501 .destroy = _destroy,
502 },
503 .next_payload = NO_PAYLOAD,
504 .proposals = linked_list_create(),
505 .type = type,
506 /* for IKEv1 only */
507 .doi = IKEV1_DOI_IPSEC,
508 .situation = SIT_IDENTITY_ONLY,
509 );
510
511 compute_length(this);
512
513 return &this->public;
514 }
515
516 /*
517 * Described in header.
518 */
519 sa_payload_t *sa_payload_create_from_proposals_v2(linked_list_t *proposals)
520 {
521 private_sa_payload_t *this;
522 enumerator_t *enumerator;
523 proposal_t *proposal;
524
525 this = (private_sa_payload_t*)sa_payload_create(SECURITY_ASSOCIATION);
526 enumerator = proposals->create_enumerator(proposals);
527 while (enumerator->enumerate(enumerator, &proposal))
528 {
529 add_proposal_v2(this, proposal);
530 }
531 enumerator->destroy(enumerator);
532
533 return &this->public;
534 }
535
536 /*
537 * Described in header.
538 */
539 sa_payload_t *sa_payload_create_from_proposal_v2(proposal_t *proposal)
540 {
541 private_sa_payload_t *this;
542
543 this = (private_sa_payload_t*)sa_payload_create(SECURITY_ASSOCIATION);
544 add_proposal_v2(this, proposal);
545
546 return &this->public;
547
548 }
549
550 /*
551 * Described in header.
552 */
553 sa_payload_t *sa_payload_create_from_proposals_v1(linked_list_t *proposals,
554 u_int32_t lifetime, u_int64_t lifebytes,
555 auth_method_t auth, ipsec_mode_t mode, bool udp,
556 u_int16_t cpi)
557 {
558 proposal_substructure_t *substruct;
559 private_sa_payload_t *this;
560
561 this = (private_sa_payload_t*)sa_payload_create(SECURITY_ASSOCIATION_V1);
562
563 /* IKEv1 encodes multiple proposals in a single substructure
564 * TODO-IKEv1: Encode ESP+AH proposals in two substructs with same num */
565 substruct = proposal_substructure_create_from_proposals_v1(proposals,
566 lifetime, lifebytes, auth, mode, udp);
567 this->proposals->insert_last(this->proposals, substruct);
568 substruct->set_is_last_proposal(substruct, FALSE);
569 if (cpi)
570 {
571 u_int8_t proposal_number = substruct->get_proposal_number(substruct);
572
573 substruct = proposal_substructure_create_for_ipcomp_v1(lifetime,
574 lifebytes, cpi, proposal_number);
575 this->proposals->insert_last(this->proposals, substruct);
576 substruct->set_is_last_proposal(substruct, FALSE);
577 /* add the proposals again without IPComp */
578 substruct = proposal_substructure_create_from_proposals_v1(proposals,
579 lifetime, lifebytes, auth, mode, udp);
580 substruct->set_proposal_number(substruct, proposal_number + 1);
581 this->proposals->insert_last(this->proposals, substruct);
582 }
583 substruct->set_is_last_proposal(substruct, TRUE);
584 compute_length(this);
585
586 return &this->public;
587 }
588
589 /*
590 * Described in header.
591 */
592 sa_payload_t *sa_payload_create_from_proposal_v1(proposal_t *proposal,
593 u_int32_t lifetime, u_int64_t lifebytes,
594 auth_method_t auth, ipsec_mode_t mode, bool udp,
595 u_int16_t cpi)
596 {
597 private_sa_payload_t *this;
598 linked_list_t *proposals;
599
600 proposals = linked_list_create();
601 proposals->insert_last(proposals, proposal);
602 this = (private_sa_payload_t*)sa_payload_create_from_proposals_v1(proposals,
603 lifetime, lifebytes, auth, mode, udp, cpi);
604 proposals->destroy(proposals);
605 return &this->public;
606 }