ikev1: Get and set the lifetimes of the selected proposal/transform
[strongswan.git] / src / libcharon / encoding / payloads / sa_payload.c
1 /*
2 * Copyright (C) 2012-2020 Tobias Brunner
3 * Copyright (C) 2005-2010 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * HSR 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 <collections/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 uint8_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 uint16_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 uint32_t doi;
75
76 /**
77 * IKEv1 situation
78 */
79 uint32_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 + PLV1_PROPOSAL_SUBSTRUCTURE,
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 + PLV2_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 == PLV2_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 == PLV1_SECURITY_ASSOCIATION)
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 == PLV1_SECURITY_ASSOCIATION)
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 == PLV1_SECURITY_ASSOCIATION)
299 { /* IKEv1 proposals may start with 0 or 1 (or any other number really) */
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 int current_number = substruct->get_proposal_number(substruct);
313
314 /* check if a proposal has a single protocol */
315 if (current_number == struct_number)
316 {
317 if (ignore_struct_number < struct_number)
318 { /* remove an already added substruct, if first of series */
319 substructs->remove_last(substructs, (void**)&substruct);
320 ignore_struct_number = struct_number;
321 }
322 continue;
323 }
324 /* for IKEv1 the numbers don't have to be consecutive, for IKEv2 they do
325 * but since we don't really care for the actual number we accept them
326 * anyway. we already verified that they increase monotonically. */
327 struct_number = current_number;
328 substructs->insert_last(substructs, substruct);
329 }
330 enumerator->destroy(enumerator);
331
332 /* generate proposals from substructs */
333 list = linked_list_create();
334 enumerator = substructs->create_enumerator(substructs);
335 while (enumerator->enumerate(enumerator, &substruct))
336 {
337 substruct->get_proposals(substruct, list);
338 }
339 enumerator->destroy(enumerator);
340 substructs->destroy(substructs);
341 return list;
342 }
343
344 METHOD(sa_payload_t, get_ipcomp_proposals, linked_list_t*,
345 private_sa_payload_t *this, uint16_t *cpi)
346 {
347 int current_proposal = -1, unsupported_proposal = -1;
348 enumerator_t *enumerator;
349 proposal_substructure_t *substruct, *espah = NULL, *ipcomp = NULL;
350 linked_list_t *list;
351
352 /* we currently only support the combination ESP|AH+IPComp, find the first */
353 enumerator = this->proposals->create_enumerator(this->proposals);
354 while (enumerator->enumerate(enumerator, &substruct))
355 {
356 uint8_t proposal_number = substruct->get_proposal_number(substruct);
357 uint8_t protocol_id = substruct->get_protocol_id(substruct);
358
359 if (proposal_number == unsupported_proposal)
360 {
361 continue;
362 }
363 if (protocol_id != PROTO_ESP && protocol_id != PROTO_AH &&
364 protocol_id != PROTO_IPCOMP)
365 { /* unsupported combination */
366 espah = ipcomp = NULL;
367 unsupported_proposal = current_proposal;
368 continue;
369 }
370 if (proposal_number != current_proposal)
371 { /* start of a new proposal */
372 if (espah && ipcomp && ipcomp->get_cpi(ipcomp, NULL))
373 { /* previous proposal is valid */
374 break;
375 }
376 espah = ipcomp = NULL;
377 current_proposal = proposal_number;
378 }
379 switch (protocol_id)
380 {
381 case PROTO_ESP:
382 case PROTO_AH:
383 espah = substruct;
384 break;
385 case PROTO_IPCOMP:
386 ipcomp = substruct;
387 break;
388 }
389 }
390 enumerator->destroy(enumerator);
391
392 list = linked_list_create();
393 if (espah && ipcomp && ipcomp->get_cpi(ipcomp, cpi))
394 {
395 espah->get_proposals(espah, list);
396 }
397 return list;
398 }
399
400 METHOD(sa_payload_t, create_substructure_enumerator, enumerator_t*,
401 private_sa_payload_t *this)
402 {
403 return this->proposals->create_enumerator(this->proposals);
404 }
405
406 METHOD(sa_payload_t, get_lifetime, uint32_t,
407 private_sa_payload_t *this, proposal_t *proposal)
408 {
409 proposal_substructure_t *substruct;
410 enumerator_t *enumerator;
411 uint8_t number = proposal->get_number(proposal);
412 uint32_t lifetime = 0;
413
414 enumerator = this->proposals->create_enumerator(this->proposals);
415 while (enumerator->enumerate(enumerator, &substruct))
416 {
417 if (substruct->get_proposal_number(substruct) == number)
418 {
419 lifetime = substruct->get_lifetime(substruct,
420 proposal->get_transform_number(proposal));
421 break;
422 }
423 }
424 enumerator->destroy(enumerator);
425
426 return lifetime;
427 }
428
429 METHOD(sa_payload_t, get_lifebytes, uint64_t,
430 private_sa_payload_t *this, proposal_t *proposal)
431 {
432 proposal_substructure_t *substruct;
433 enumerator_t *enumerator;
434 uint8_t number = proposal->get_number(proposal);
435 uint64_t lifebytes = 0;
436
437 enumerator = this->proposals->create_enumerator(this->proposals);
438 while (enumerator->enumerate(enumerator, &substruct))
439 {
440 if (substruct->get_proposal_number(substruct) == number)
441 {
442 lifebytes = substruct->get_lifebytes(substruct,
443 proposal->get_transform_number(proposal));
444 break;
445 }
446 }
447 enumerator->destroy(enumerator);
448
449 return lifebytes;
450 }
451
452 METHOD(sa_payload_t, get_auth_method, auth_method_t,
453 private_sa_payload_t *this)
454 {
455 proposal_substructure_t *substruct;
456 enumerator_t *enumerator;
457 auth_method_t method = AUTH_NONE;
458
459 enumerator = this->proposals->create_enumerator(this->proposals);
460 if (enumerator->enumerate(enumerator, &substruct))
461 {
462 method = substruct->get_auth_method(substruct);
463 }
464 enumerator->destroy(enumerator);
465
466 return method;
467 }
468
469 METHOD(sa_payload_t, get_encap_mode, ipsec_mode_t,
470 private_sa_payload_t *this, bool *udp)
471 {
472 proposal_substructure_t *substruct;
473 enumerator_t *enumerator;
474 ipsec_mode_t mode = MODE_NONE;
475
476 enumerator = this->proposals->create_enumerator(this->proposals);
477 if (enumerator->enumerate(enumerator, &substruct))
478 {
479 mode = substruct->get_encap_mode(substruct, udp);
480 }
481 enumerator->destroy(enumerator);
482
483 return mode;
484 }
485
486 METHOD2(payload_t, sa_payload_t, destroy, void,
487 private_sa_payload_t *this)
488 {
489 this->proposals->destroy_offset(this->proposals,
490 offsetof(payload_t, destroy));
491 free(this);
492 }
493
494 /*
495 * Described in header.
496 */
497 sa_payload_t *sa_payload_create(payload_type_t type)
498 {
499 private_sa_payload_t *this;
500
501 INIT(this,
502 .public = {
503 .payload_interface = {
504 .verify = _verify,
505 .get_encoding_rules = _get_encoding_rules,
506 .get_header_length = _get_header_length,
507 .get_length = _get_length,
508 .get_next_type = _get_next_type,
509 .set_next_type = _set_next_type,
510 .get_type = _get_type,
511 .destroy = _destroy,
512 },
513 .get_proposals = _get_proposals,
514 .get_ipcomp_proposals = _get_ipcomp_proposals,
515 .create_substructure_enumerator = _create_substructure_enumerator,
516 .get_lifetime = _get_lifetime,
517 .get_lifebytes = _get_lifebytes,
518 .get_auth_method = _get_auth_method,
519 .get_encap_mode = _get_encap_mode,
520 .destroy = _destroy,
521 },
522 .next_payload = PL_NONE,
523 .proposals = linked_list_create(),
524 .type = type,
525 /* for IKEv1 only */
526 .doi = IKEV1_DOI_IPSEC,
527 .situation = SIT_IDENTITY_ONLY,
528 );
529
530 compute_length(this);
531
532 return &this->public;
533 }
534
535 /*
536 * Described in header.
537 */
538 sa_payload_t *sa_payload_create_from_proposals_v2(linked_list_t *proposals)
539 {
540 private_sa_payload_t *this;
541 enumerator_t *enumerator;
542 proposal_t *proposal;
543
544 this = (private_sa_payload_t*)sa_payload_create(PLV2_SECURITY_ASSOCIATION);
545 enumerator = proposals->create_enumerator(proposals);
546 while (enumerator->enumerate(enumerator, &proposal))
547 {
548 add_proposal_v2(this, proposal);
549 }
550 enumerator->destroy(enumerator);
551
552 return &this->public;
553 }
554
555 /*
556 * Described in header.
557 */
558 sa_payload_t *sa_payload_create_from_proposal_v2(proposal_t *proposal)
559 {
560 private_sa_payload_t *this;
561
562 this = (private_sa_payload_t*)sa_payload_create(PLV2_SECURITY_ASSOCIATION);
563 add_proposal_v2(this, proposal);
564
565 return &this->public;
566
567 }
568
569 /*
570 * Described in header.
571 */
572 sa_payload_t *sa_payload_create_from_proposals_v1(linked_list_t *proposals,
573 uint32_t lifetime, uint64_t lifebytes,
574 auth_method_t auth, ipsec_mode_t mode,
575 encap_t udp, uint16_t cpi)
576 {
577 proposal_substructure_t *substruct;
578 private_sa_payload_t *this;
579
580 this = (private_sa_payload_t*)sa_payload_create(PLV1_SECURITY_ASSOCIATION);
581
582 if (!proposals || !proposals->get_count(proposals))
583 {
584 return &this->public;
585 }
586
587 /* IKEv1 encodes multiple proposals in a single substructure
588 * TODO-IKEv1: Encode ESP+AH proposals in two substructs with same num */
589 substruct = proposal_substructure_create_from_proposals_v1(proposals,
590 lifetime, lifebytes, auth, mode, udp);
591 this->proposals->insert_last(this->proposals, substruct);
592 substruct->set_is_last_proposal(substruct, FALSE);
593 if (cpi)
594 {
595 uint8_t proposal_number = substruct->get_proposal_number(substruct);
596
597 substruct = proposal_substructure_create_for_ipcomp_v1(lifetime,
598 lifebytes, cpi, mode, udp, proposal_number);
599 this->proposals->insert_last(this->proposals, substruct);
600 substruct->set_is_last_proposal(substruct, FALSE);
601 /* add the proposals again without IPComp */
602 substruct = proposal_substructure_create_from_proposals_v1(proposals,
603 lifetime, lifebytes, auth, mode, udp);
604 substruct->set_proposal_number(substruct, proposal_number + 1);
605 this->proposals->insert_last(this->proposals, substruct);
606 }
607 substruct->set_is_last_proposal(substruct, TRUE);
608 compute_length(this);
609
610 return &this->public;
611 }
612
613 /*
614 * Described in header.
615 */
616 sa_payload_t *sa_payload_create_from_proposal_v1(proposal_t *proposal,
617 uint32_t lifetime, uint64_t lifebytes,
618 auth_method_t auth, ipsec_mode_t mode,
619 encap_t udp, uint16_t cpi)
620 {
621 private_sa_payload_t *this;
622 linked_list_t *proposals;
623
624 proposals = linked_list_create();
625 proposals->insert_last(proposals, proposal);
626 this = (private_sa_payload_t*)sa_payload_create_from_proposals_v1(proposals,
627 lifetime, lifebytes, auth, mode, udp, cpi);
628 proposals->destroy(proposals);
629 return &this->public;
630 }