Migrated transform_attribute to INIT/METHOD macros
[strongswan.git] / src / libcharon / encoding / payloads / proposal_substructure.c
1 /*
2 * Copyright (C) 2005-2010 Martin Willi
3 * Copyright (C) 2005 Jan Hutter
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <stddef.h>
18
19 #include "proposal_substructure.h"
20
21 #include <encoding/payloads/encodings.h>
22 #include <encoding/payloads/transform_substructure.h>
23 #include <library.h>
24 #include <utils/linked_list.h>
25 #include <daemon.h>
26
27 /**
28 * IKEv1 Value for a proposal payload.
29 */
30 #define PROPOSAL_TYPE_VALUE 2
31
32 typedef struct private_proposal_substructure_t private_proposal_substructure_t;
33
34 /**
35 * Private data of an proposal_substructure_t object.
36 */
37 struct private_proposal_substructure_t {
38
39 /**
40 * Public proposal_substructure_t interface.
41 */
42 proposal_substructure_t public;
43
44 /**
45 * Next payload type.
46 */
47 u_int8_t next_payload;
48
49 /**
50 * Length of this payload.
51 */
52 u_int16_t proposal_length;
53
54 /**
55 * Proposal number.
56 */
57 u_int8_t proposal_number;
58
59 /**
60 * Protocol ID.
61 */
62 u_int8_t protocol_id;
63
64 /**
65 * SPI size of the following SPI.
66 */
67 u_int8_t spi_size;
68
69 /**
70 * Number of transforms.
71 */
72 u_int8_t transforms_count;
73
74 /**
75 * SPI is stored as chunk.
76 */
77 chunk_t spi;
78
79 /**
80 * Transforms are stored in a linked_list_t.
81 */
82 linked_list_t * transforms;
83 };
84
85 /**
86 * Encoding rules to parse or generate a Proposal substructure.
87 *
88 * The defined offsets are the positions in a object of type
89 * private_proposal_substructure_t.
90 */
91 encoding_rule_t proposal_substructure_encodings[] = {
92 /* 1 Byte next payload type, stored in the field next_payload */
93 { U_INT_8, offsetof(private_proposal_substructure_t, next_payload) },
94 /* Reserved Byte is skipped */
95 { RESERVED_BYTE, 0 },
96 /* Length of the whole proposal substructure payload*/
97 { PAYLOAD_LENGTH, offsetof(private_proposal_substructure_t, proposal_length) },
98 /* proposal number is a number of 8 bit */
99 { U_INT_8, offsetof(private_proposal_substructure_t, proposal_number) },
100 /* protocol ID is a number of 8 bit */
101 { U_INT_8, offsetof(private_proposal_substructure_t, protocol_id) },
102 /* SPI Size has its own type */
103 { SPI_SIZE, offsetof(private_proposal_substructure_t, spi_size) },
104 /* Number of transforms is a number of 8 bit */
105 { U_INT_8, offsetof(private_proposal_substructure_t, transforms_count) },
106 /* SPI is a chunk of variable size*/
107 { SPI, offsetof(private_proposal_substructure_t, spi) },
108 /* Transforms are stored in a transform substructure,
109 offset points to a linked_list_t pointer */
110 { TRANSFORMS, offsetof(private_proposal_substructure_t, transforms) }
111 };
112
113 /*
114 1 2 3
115 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
116 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
117 ! 0 (last) or 2 ! RESERVED ! Proposal Length !
118 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119 ! Proposal # ! Protocol ID ! SPI Size !# of Transforms!
120 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
121 ~ SPI (variable) ~
122 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
123 ! !
124 ~ <Transforms> ~
125 ! !
126 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
127 */
128
129 METHOD(payload_t, verify, status_t,
130 private_proposal_substructure_t *this)
131 {
132 status_t status = SUCCESS;
133 enumerator_t *enumerator;
134 payload_t *current;
135
136 if (this->next_payload != NO_PAYLOAD && this->next_payload != 2)
137 {
138 /* must be 0 or 2 */
139 DBG1(DBG_ENC, "inconsistent next payload");
140 return FAILED;
141 }
142 if (this->transforms_count != this->transforms->get_count(this->transforms))
143 {
144 /* must be the same! */
145 DBG1(DBG_ENC, "transform count invalid");
146 return FAILED;
147 }
148
149 switch (this->protocol_id)
150 {
151 case PROTO_AH:
152 case PROTO_ESP:
153 if (this->spi.len != 4)
154 {
155 DBG1(DBG_ENC, "invalid SPI length in %N proposal",
156 protocol_id_names, this->protocol_id);
157 return FAILED;
158 }
159 break;
160 case PROTO_IKE:
161 if (this->spi.len != 0 && this->spi.len != 8)
162 {
163 DBG1(DBG_ENC, "invalid SPI length in IKE proposal");
164 return FAILED;
165 }
166 break;
167 default:
168 break;
169 }
170 enumerator = this->transforms->create_enumerator(this->transforms);
171 while (enumerator->enumerate(enumerator, &current))
172 {
173 status = current->verify(current);
174 if (status != SUCCESS)
175 {
176 DBG1(DBG_ENC, "TRANSFORM_SUBSTRUCTURE verification failed");
177 break;
178 }
179 }
180 enumerator->destroy(enumerator);
181
182 /* proposal number is checked in SA payload */
183 return status;
184 }
185
186 METHOD(payload_t, get_encoding_rules, void,
187 private_proposal_substructure_t *this, encoding_rule_t **rules,
188 size_t *rule_count)
189 {
190 *rules = proposal_substructure_encodings;
191 *rule_count = countof(proposal_substructure_encodings);
192 }
193
194 METHOD(payload_t, get_type, payload_type_t,
195 private_proposal_substructure_t *this)
196 {
197 return PROPOSAL_SUBSTRUCTURE;
198 }
199
200 METHOD(payload_t, get_next_type, payload_type_t,
201 private_proposal_substructure_t *this)
202 {
203 return this->next_payload;
204 }
205
206 METHOD(payload_t, set_next_type, void,
207 private_proposal_substructure_t *this, payload_type_t type)
208 {
209 }
210
211 /**
212 * (re-)compute the length of the payload.
213 */
214 static void compute_length(private_proposal_substructure_t *this)
215 {
216 iterator_t *iterator;
217 payload_t *current_transform;
218 size_t transforms_count = 0;
219 size_t length = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH;
220
221 iterator = this->transforms->create_iterator(this->transforms,TRUE);
222 while (iterator->iterate(iterator, (void**)&current_transform))
223 {
224 length += current_transform->get_length(current_transform);
225 transforms_count++;
226 }
227 iterator->destroy(iterator);
228
229 length += this->spi.len;
230 this->transforms_count = transforms_count;
231 this->proposal_length = length;
232 }
233
234 METHOD(payload_t, get_length, size_t,
235 private_proposal_substructure_t *this)
236 {
237 return this->proposal_length;
238 }
239
240 /**
241 * Add a transform substructure to the proposal
242 */
243 static void add_transform_substructure(private_proposal_substructure_t *this,
244 transform_substructure_t *transform)
245 {
246 if (this->transforms->get_count(this->transforms) > 0)
247 {
248 transform_substructure_t *last;
249
250 this->transforms->get_last(this->transforms, (void **)&last);
251 last->set_is_last_transform(last, FALSE);
252 }
253 transform->set_is_last_transform(transform,TRUE);
254 this->transforms->insert_last(this->transforms, transform);
255 compute_length(this);
256 }
257
258 METHOD(proposal_substructure_t, set_is_last_proposal, void,
259 private_proposal_substructure_t *this, bool is_last)
260 {
261 this->next_payload = is_last ? 0 : PROPOSAL_TYPE_VALUE;
262 }
263
264 METHOD(proposal_substructure_t, set_proposal_number, void,
265 private_proposal_substructure_t *this,u_int8_t proposal_number)
266 {
267 this->proposal_number = proposal_number;
268 }
269
270 METHOD(proposal_substructure_t, get_proposal_number, u_int8_t,
271 private_proposal_substructure_t *this)
272 {
273 return this->proposal_number;
274 }
275
276 METHOD(proposal_substructure_t, set_protocol_id, void,
277 private_proposal_substructure_t *this,u_int8_t protocol_id)
278 {
279 this->protocol_id = protocol_id;
280 }
281
282 METHOD(proposal_substructure_t, get_protocol_id, u_int8_t,
283 private_proposal_substructure_t *this)
284 {
285 return this->protocol_id;
286 }
287
288 METHOD(proposal_substructure_t, set_spi, void,
289 private_proposal_substructure_t *this, chunk_t spi)
290 {
291 free(this->spi.ptr);
292 this->spi = chunk_clone(spi);
293 this->spi_size = spi.len;
294 compute_length(this);
295 }
296
297 METHOD(proposal_substructure_t, get_spi, chunk_t,
298 private_proposal_substructure_t *this)
299 {
300 return this->spi;
301 }
302
303 METHOD(proposal_substructure_t, get_proposal, proposal_t*,
304 private_proposal_substructure_t *this)
305 {
306 enumerator_t *enumerator;
307 transform_substructure_t *transform;
308 proposal_t *proposal;
309 u_int64_t spi;
310
311 proposal = proposal_create(this->protocol_id, this->proposal_number);
312
313 enumerator = this->transforms->create_enumerator(this->transforms);
314 while (enumerator->enumerate(enumerator, &transform))
315 {
316 transform_type_t transform_type;
317 u_int16_t transform_id;
318 u_int16_t key_length = 0;
319
320 transform_type = transform->get_transform_type(transform);
321 transform_id = transform->get_transform_id(transform);
322 transform->get_key_length(transform, &key_length);
323
324 proposal->add_algorithm(proposal, transform_type, transform_id, key_length);
325 }
326 enumerator->destroy(enumerator);
327
328 switch (this->spi.len)
329 {
330 case 4:
331 spi = *((u_int32_t*)this->spi.ptr);
332 break;
333 case 8:
334 spi = *((u_int64_t*)this->spi.ptr);
335 break;
336 default:
337 spi = 0;
338 }
339 proposal->set_spi(proposal, spi);
340
341 return proposal;
342 }
343
344 METHOD(proposal_substructure_t, clone_, proposal_substructure_t*,
345 private_proposal_substructure_t *this)
346 {
347 private_proposal_substructure_t *clone;
348 enumerator_t *enumerator;
349 transform_substructure_t *current;
350
351 clone = (private_proposal_substructure_t*)proposal_substructure_create();
352 clone->next_payload = this->next_payload;
353 clone->proposal_number = this->proposal_number;
354 clone->protocol_id = this->protocol_id;
355 clone->spi_size = this->spi_size;
356 if (this->spi.ptr != NULL)
357 {
358 clone->spi.ptr = clalloc(this->spi.ptr, this->spi.len);
359 clone->spi.len = this->spi.len;
360 }
361 enumerator = this->transforms->create_enumerator(this->transforms);
362 while (enumerator->enumerate(enumerator, &current))
363 {
364 current = current->clone(current);
365 add_transform_substructure(clone, current);
366 }
367 enumerator->destroy(enumerator);
368 compute_length(clone);
369
370 return &clone->public;
371 }
372
373 METHOD2(payload_t, proposal_substructure_t, destroy, void,
374 private_proposal_substructure_t *this)
375 {
376 this->transforms->destroy_offset(this->transforms,
377 offsetof(transform_substructure_t, destroy));
378 chunk_free(&this->spi);
379 free(this);
380 }
381
382 /*
383 * Described in header.
384 */
385 proposal_substructure_t *proposal_substructure_create()
386 {
387 private_proposal_substructure_t *this;
388
389 INIT(this,
390 .public = {
391 .payload_interface = {
392 .verify = _verify,
393 .get_encoding_rules = _get_encoding_rules,
394 .get_length = _get_length,
395 .get_next_type = _get_next_type,
396 .set_next_type = _set_next_type,
397 .get_type = _get_type,
398 .destroy = _destroy,
399 },
400 .set_proposal_number = _set_proposal_number,
401 .get_proposal_number = _get_proposal_number,
402 .set_protocol_id = _set_protocol_id,
403 .get_protocol_id = _get_protocol_id,
404 .set_is_last_proposal = _set_is_last_proposal,
405 .get_proposal = _get_proposal,
406 .set_spi = _set_spi,
407 .get_spi = _get_spi,
408 .clone = _clone_,
409 .destroy = _destroy,
410 },
411 .next_payload = NO_PAYLOAD,
412 .proposal_length = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH,
413 .transforms = linked_list_create(),
414 );
415
416 return &this->public;
417 }
418
419 /*
420 * Described in header.
421 */
422 proposal_substructure_t *proposal_substructure_create_from_proposal(
423 proposal_t *proposal)
424 {
425 transform_substructure_t *transform;
426 private_proposal_substructure_t *this;
427 u_int16_t alg, key_size;
428 enumerator_t *enumerator;
429
430 this = (private_proposal_substructure_t*)proposal_substructure_create();
431
432 /* encryption algorithm is only availble in ESP */
433 enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM);
434 while (enumerator->enumerate(enumerator, &alg, &key_size))
435 {
436 transform = transform_substructure_create_type(ENCRYPTION_ALGORITHM,
437 alg, key_size);
438 add_transform_substructure(this, transform);
439 }
440 enumerator->destroy(enumerator);
441
442 /* integrity algorithms */
443 enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM);
444 while (enumerator->enumerate(enumerator, &alg, &key_size))
445 {
446 transform = transform_substructure_create_type(INTEGRITY_ALGORITHM,
447 alg, key_size);
448 add_transform_substructure(this, transform);
449 }
450 enumerator->destroy(enumerator);
451
452 /* prf algorithms */
453 enumerator = proposal->create_enumerator(proposal, PSEUDO_RANDOM_FUNCTION);
454 while (enumerator->enumerate(enumerator, &alg, &key_size))
455 {
456 transform = transform_substructure_create_type(PSEUDO_RANDOM_FUNCTION,
457 alg, key_size);
458 add_transform_substructure(this, transform);
459 }
460 enumerator->destroy(enumerator);
461
462 /* dh groups */
463 enumerator = proposal->create_enumerator(proposal, DIFFIE_HELLMAN_GROUP);
464 while (enumerator->enumerate(enumerator, &alg, NULL))
465 {
466 transform = transform_substructure_create_type(DIFFIE_HELLMAN_GROUP,
467 alg, 0);
468 add_transform_substructure(this, transform);
469 }
470 enumerator->destroy(enumerator);
471
472 /* extended sequence numbers */
473 enumerator = proposal->create_enumerator(proposal, EXTENDED_SEQUENCE_NUMBERS);
474 while (enumerator->enumerate(enumerator, &alg, NULL))
475 {
476 transform = transform_substructure_create_type(EXTENDED_SEQUENCE_NUMBERS,
477 alg, 0);
478 add_transform_substructure(this, transform);
479 }
480 enumerator->destroy(enumerator);
481
482 /* add SPI, if necessary */
483 switch (proposal->get_protocol(proposal))
484 {
485 case PROTO_AH:
486 case PROTO_ESP:
487 this->spi_size = this->spi.len = 4;
488 this->spi.ptr = malloc(this->spi_size);
489 *((u_int32_t*)this->spi.ptr) = proposal->get_spi(proposal);
490 break;
491 case PROTO_IKE:
492 if (proposal->get_spi(proposal))
493 { /* IKE only uses SPIS when rekeying, but on initial setup */
494 this->spi_size = this->spi.len = 8;
495 this->spi.ptr = malloc(this->spi_size);
496 *((u_int64_t*)this->spi.ptr) = proposal->get_spi(proposal);
497 }
498 break;
499 default:
500 break;
501 }
502 this->proposal_number = proposal->get_number(proposal);
503 this->protocol_id = proposal->get_protocol(proposal);
504 compute_length(this);
505
506 return &this->public;
507 }